Parcourir la source

短剧统计和染色时间配置功能点优化等

pansl il y a 1 an
Parent
commit
986923745d

+ 4 - 11
src/App.vue

@@ -54,25 +54,18 @@ function getBreadcrumbs(newRoute: RouteLocationNormalizedLoaded) {
 
 <style lang="scss">
 ::-webkit-scrollbar {
-  width: 6px;
-  height: 8px;
-  background-color: #ebeef5;
+  width: 5px;
+  height: 5px;
 }
 
 ::-webkit-scrollbar-thumb {
   border-radius: 30px;
-  box-shadow: inset 0 0 6px rgba(0, 0, 0, .3);
-  -webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, .3);
-  background-color: #ccc;
-}
-
-::-webkit-scrollbar-thumb:hover {
-  background-color: rgba($color: #000000, $alpha: .5);
+  box-shadow: inset 0 0 6px rgba(0, 0, 0, .2);
+  -webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, .2);
 }
 
 ::-webkit-scrollbar-track {
   box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.2);
   border-radius: 3px;
-  background: rgba(255, 255, 255, 1);
 }
 </style>

+ 15 - 1
src/api/applet/index.ts

@@ -63,8 +63,22 @@ export function manageMiniprogramAllocationDetail(
 }
 
 /**
- *
+ *获取微信授权
  */
 export function channelOpenPlatformPreauth(params?: object) {
   return http.get(`/channel/openPlatform/preauth`, params);
 }
+
+/**
+ * 获取染色时间
+ */
+export function getRanseDuration(params: object) {
+  return http.get('/manage/miniprogram/ranseConfig/getRanseDuration', params);
+}
+
+/**
+ * 设置染色时间
+ */
+export function setRanseDuration(params: object) {
+  return http.post('/manage/miniprogram/ranseConfig/setRanseDuration', params);
+}

+ 82 - 0
src/api/officialAccount/keywordReply/index.ts

@@ -0,0 +1,82 @@
+import http from '@/api/http';
+/**
+ * 关键词-列表
+ */
+export function wechatPlatformKeywordList(params: object) {
+  return http.get(`/wechatPlatform/keyword/list`, params);
+}
+/**
+ * 关键词-详情
+ */
+export function wechatPlatformKeywordDetail(
+  id: number | string,
+  params?: object
+) {
+  return http.get(`/wechatPlatform/keyword/detail/${id}`, params);
+}
+/**
+ * 关键词-配置公众号列表
+ */
+export function wechatPlatformKeywordAuthList(
+  id: number | string,
+  params?: object
+) {
+  return http.get(`/wechatPlatform/keyword/auth_list/${id}`, params);
+}
+
+/**
+ * 获取小程序播放页面链接
+ */
+export function wechatPlatformCommonGetPlayUrl(params: object) {
+  return http.post('/wechatPlatform/common/get_play_url', params);
+}
+/**
+ * 关键词-添加
+ */
+export function wechatPlatformKeywordAdd(params: object) {
+  return http.post('/wechatPlatform/keyword/add', params);
+}
+/**
+ * 关键词-编辑保存
+ */
+export function wechatPlatformKeywordEdit(id: number | string, params: object) {
+  return http.post(`/wechatPlatform/keyword/edit/${id}`, params);
+}
+/**
+ * 关键词-删除
+ */
+export function wechatPlatformKeywordDel(params: object) {
+  return http.post(`/wechatPlatform/keyword/del`, params);
+}
+/**
+ * 关键词-分配公众号
+ */
+export function wechatPlatformKeywordAllocation(
+  id: number | string,
+  params: object
+) {
+  return http.post(`/wechatPlatform/keyword/allocation/${id}`, params);
+}
+
+/**
+ * 关键字-全局设置获取
+ */
+export function wechatPlatformKeywordGetConfig(
+  miniprogramId: number | string,
+  params?: object
+) {
+  return http.get(`/wechatPlatform/keyword/getConfig/${miniprogramId}`, params);
+}
+
+/**
+ * 关键字-全局设置更新
+ */
+export function wechatPlatformKeywordGetConfigSet(
+  miniprogramId: number | string,
+  params: object
+) {
+  return http.post(
+    `/wechatPlatform/keyword/getConfig/${miniprogramId}`,
+    params
+  );
+}

+ 7 - 0
src/api/officialAccount/officialList/index.ts

@@ -0,0 +1,7 @@
+import http from '@/api/http';
+/**
+ * 公众号列表
+ */
+export function wechatPlatformOfficialAccountList(params: object) {
+  return http.get(`/wechatPlatform/officialAccount/list`, params);
+}

+ 3 - 4
src/api/promotion/ranse.ts

@@ -3,14 +3,13 @@ import http from '@/api/http';
 /**
  * 获取染色时间
  */
-export function getRanseDuration() {
-  return http.get('/tuiguang/ranseConfig/getRanseDuration');
+export function getRanseDuration(params: object) {
+  return http.get('/tuiguang/ranseConfig/getRanseDuration', params);
 }
 
 /**
  * 设置染色时间
  */
 export function setRanseDuration(params: object) {
-  return http.post('/tuiguang/ranseConfig/setRanseDuration',params);
+  return http.post('/tuiguang/ranseConfig/setRanseDuration', params);
 }
-

+ 0 - 2
src/layout/components/content.vue

@@ -7,8 +7,6 @@
     <!-- Container -->
     <div class="h-screen max-w-full p-1 overflow-auto sm:p-2 sm:overflow-x-hidden" id="content">
       <router-view />
-
-      <!--<div class="w-full h-10 mt-2 leading-10 text-center text-gray-400">内容中台管理系统 @copyright 2018 ~ {{ year }}</div>-->
     </div>
   </div>
 </template>

+ 53 - 40
src/router/modules/charge.ts

@@ -1,41 +1,54 @@
-// import { RouteRecordRaw } from 'vue-router';
-// // eslint-disable-next-line @typescript-eslint/ban-ts-comment
-// // @ts-ignore
-// const router: RouteRecordRaw[] = [
-//   {
-//     path: '/charge',
-//     component: () => import('@/layout/index.vue'),
-//     meta: { title: '回传管理', icon: 'user' },
-//     children: [
-//       {
-//         path: 'index',
-//         name: 'user-account11',
-//         meta: { title: '巨量2.0事件-微信小程序', icon: 'home' },
-//         component: () => import('@/views/payBack/juliangPlus/index.vue')
-//       },
-//       {
-//         path: 'index1',
-//         name: 'user-account',
-//         meta: { title: '被关注回复', icon: 'home' },
-//         component: () =>
-//           import('@/views/officialAccount/attentionReply/index.vue')
-//       },
-//       {
-//         path: 'index3',
-//         name: 'user-account33',
-//         meta: { title: '关键字回复', icon: 'home' },
-//         component: () =>
-//           import('@/views/officialAccount/keywordReply/index.vue')
-//       },
-//       {
-//         path: 'index2',
-//         name: 'user-account123',
-//         meta: { title: '菜单', icon: 'home', hidden: true },
-//         component: () =>
-//           import('@/views/officialAccount/publicCustomMenu/index.vue')
-//       }
-//     ]
-//   }
-// ];
+import { RouteRecordRaw } from 'vue-router';
+// eslint-disable-next-line @typescript-eslint/ban-ts-comment
+// @ts-ignore
+const router: RouteRecordRaw[] = [
+  {
+    path: '/charge',
+    component: () => import('@/layout/index.vue'),
+    meta: { title: '回传管理', icon: 'user' },
+    children: [
+      {
+        path: 'index',
+        name: 'user-account11',
+        meta: { title: '巨量2.0事件-微信小程序', icon: 'home' },
+        component: () => import('@/views/payBack/juliangPlus/index.vue')
+      },
+      {
+        path: 'index1',
+        name: 'user-account',
+        meta: { title: '被关注回复', icon: 'home' },
+        component: () =>
+          import('@/views/officialAccount/attentionReply/index.vue')
+      },
+      {
+        path: 'index3',
+        name: 'user-account33',
+        meta: { title: '关键字回复', icon: 'home' },
+        component: () =>
+          import('@/views/officialAccount/keywordReply/index.vue')
+      },
+      {
+        path: 'index4',
+        name: 'user-account44',
+        meta: { title: '公众号列表', icon: 'home' },
+        component: () =>
+          import('@/views/officialAccount/officialList/index.vue')
+      },
+      {
+        path: 'index5',
+        name: 'user-account55',
+        meta: { title: '客服消息', icon: 'home' },
+        component: () => import('@/views/officialAccount/newsService/index.vue')
+      },
+      {
+        path: 'index2',
+        name: 'user-account123',
+        meta: { title: '菜单', icon: 'home', hidden: true },
+        component: () =>
+          import('@/views/officialAccount/publicCustomMenu/index.vue')
+      }
+    ]
+  }
+];
 
-// export default router;
+export default router;

+ 1 - 1
src/styles/var.scss

@@ -1,5 +1,5 @@
 :root {
-    --el-menu-base-level-padding: 20px;
+    --el-menu-base-level-padding: 15px;
     // 后台自定义
     // el-table
     --el-table-border-radius: 8px;

+ 32 - 12
src/views/appletManage/miniProgramList/index.vue

@@ -62,22 +62,29 @@
           <el-table-column prop="type_name" label="类型" />
           <el-table-column label="操作" width="200" fixed="right">
             <template #default="scope">
-              <el-button link type="primary" size="small" @click="opendepots(scope.row)"
+              <el-button link type="primary" size="small" @click="openType('depotsVisible', scope.row)"
                 v-action="'manage.miniprogram.allocationStore'">分配</el-button>
               <el-button link type="primary" size="small" @click="open(scope.row.id)"
                 v-action="'manage.miniprogram.update'">编辑</el-button>
+              <br />
+              <el-button link type="primary" size="small" @click="openType('ranseVisible', scope.row)"
+                v-action="'manage.miniprogram.setRanseDuration'">染色规则配置</el-button>
             </template>
           </el-table-column>
         </el-table>
         <Paginate />
       </div>
       <Dialog width="800px" v-model="depotsVisible" title="分配" destroy-on-close>
-        <depotsTransfer @close="closeDeptos()" :primary="depotsData"></depotsTransfer>
+        <depotsTransfer @close="closeType('depotsVisible')" :primary="current"></depotsTransfer>
       </Dialog>
 
       <Dialog v-model="visible" :title="title" destroy-on-close>
         <Create @close="close(search)" :primary="id" :api="api" />
       </Dialog>
+
+      <Dialog v-model="ranseVisible" title="染色规则配置" destroy-on-close>
+        <Ranse @close="closeType('ranseVisible')" :primary="current" />
+      </Dialog>
     </div>
   </div>
 </template>
@@ -89,27 +96,40 @@ import depotsTransfer from './form/depotsTransfer.vue';
 import { useGetList } from '@/hook/curd/useGetList';
 import { useOpen } from '@/hook/curd/useOpen';
 import { manageMiniprogramCompanylist, manageMiniprogramTypelist } from '@/api/applet/index'
-
+import Ranse from './ranse/index.vue'
 const api = 'manage/miniprogram/index';
 const depotsVisible = ref(false)
-const depotsData = ref({})
+const ranseVisible = ref(false)
+const current = ref({})
 const companylist = ref([])
 const miniprogramTypelist = ref([])
-
 const { data, query, search, reset, loading } = useGetList(api);
 const { open, close, title, visible, id } = useOpen();
 const rolesIdentify = inject('rolesIdentify')
 const tableData = computed(() => data.value?.data);
 const isShowSecret = computed(() => !rolesIdentify.value.includes('company') && !rolesIdentify.value.includes('optimizer'))
-
-const opendepots = (data) => {
-  depotsVisible.value = true
-  depotsData.value = data
-}
-const closeDeptos = () => {
-  depotsVisible.value = false
+const closeType = (type: string) => {
+  switch (type) {
+    case 'depotsVisible':
+      depotsVisible.value = false
+      break;
+    case 'ranseVisible':
+      ranseVisible.value = false
+      break;
+  }
   search()
 }
+const openType = (type: string, data?: object) => {
+  current.value = data || {}
+  switch (type) {
+    case 'depotsVisible':
+      depotsVisible.value = true
+      break;
+    case 'ranseVisible':
+      ranseVisible.value = true
+      break;
+  }
+}
 const init = () => {
   manageMiniprogramCompanylist().then(res => {
     companylist.value = res.data

+ 102 - 0
src/views/appletManage/miniProgramList/ranse/index.vue

@@ -0,0 +1,102 @@
+<template>
+  <div class="page">
+    <div class="header">
+      <el-text class="text-notice">注意事项</el-text>
+      <el-text class="text-notice">1.充值用户>=n分钟连续未登录,如果点击新连接,会归属于新链接的优化师</el-text>
+      <el-text class="text-notice">2.未充值用户>=n分钟连续未登录,如果点击新连接,会归属于新链接的优化师</el-text>
+    </div>
+    <div class="set-box">
+      <div class="set-box-item">
+        <el-text class="lab-txt"> <label style="color: red;margin-right: 10px;">*</label>非充值用户:</el-text>
+        <el-input-number v-model="setData.no_charge_user_duration" :min="1" />
+        <el-text style="margin-left: 10px;color: #555;">分</el-text>
+      </div>
+      <div class="set-box-item">
+        <el-text class="lab-txt"> <label style="color: red;margin-right: 10px;">*</label>充值用户:</el-text>
+        <el-input-number v-model="setData.charge_user_duration" :min="1" />
+        <el-text style="margin-left: 10px;color: #555;">分</el-text>
+      </div>
+      <div class="flex justify-end">
+        <el-button style="margin-left: 140px;margin-top: 30px;" type="primary" @click="saveSetting">确定</el-button>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script lang="ts" setup>
+import { getRanseDuration, setRanseDuration } from "@/api/applet/index";
+const rolesIdentify = inject('rolesIdentify');
+const setData = ref({})
+const props = defineProps({
+  primary: String | Number,
+});
+const emit = defineEmits(['close']);
+const saveSetting = () => {
+  if (!setData.value.charge_user_duration) {
+    ElMessage.error('请填写充值用户染色时间');
+    return false;
+  }
+  if (!setData.value.no_charge_user_duration) {
+    ElMessage.error('请填写未充值用户染色时间');
+    return false;
+  }
+  if (setData.value.charge_user_duration <= setData.value.no_charge_user_duration) {
+    ElMessage.error('充值用户染色时间应大于非充值用户染色时间');
+    return false;
+  }
+  setRanseDuration({ miniprogram_id: props.primary?.id, ...setData.value }).then(res => {
+    ElMessage.success(res.message);
+    emit('close')
+  })
+}
+
+const getSetData = (params: object) => {
+  getRanseDuration(params).then(res => {
+    setData.value = res.data;
+  })
+}
+
+if (props.primary) {
+  console.log(props.primary, 'props.primaryprops.primary');
+  getSetData({ miniprogram_id: props.primary?.id });
+}
+onMounted(() => {
+
+});
+</script>
+
+<style lang="scss" scoped>
+.lab-txt {
+  display: inline-table;
+  width: 120px;
+  color: #555;
+  text-align: right;
+}
+
+.page {
+  background-color: #fff;
+}
+
+.header {
+  display: flex;
+  flex-direction: column;
+  background-color: #fcd3d3;
+  padding: 20px 30px;
+}
+
+.text-notice {
+  width: 100%;
+  color: #f56c6c;
+}
+
+.set-box {
+  margin-top: 20px;
+  display: flex;
+  flex-direction: column;
+}
+
+.set-box-item {
+  width: 100%;
+  margin-top: 20px;
+}
+</style>

+ 2 - 1
src/views/dataStatistics/shortStatistical/excelTitle.ts

@@ -6,5 +6,6 @@ export const titleObj = {
   充值金额: 'amount',
   充值次数: 'charge_count',
   充值人数: 'charge_user_num',
-  播放次数: 'play_count'
+  播放次数: 'play_count',
+  点击人数: 'click_uv'
 };

+ 18 - 0
src/views/dataStatistics/shortStatistical/index.vue

@@ -105,6 +105,24 @@
             </div>
           </template>
         </el-table-column>
+        <el-table-column label="点击人数" sortable prop="click_uv">
+          <template #header>
+            <span>点击人数</span>
+            <el-tooltip placement="top">
+              <template #content>
+                当日绑定此短剧的所有推广链接,累计点击的人数<br />
+              </template>
+              <el-icon>
+                <InfoFilled />
+              </el-icon>
+            </el-tooltip>
+          </template>
+          <template #default="scope">
+            <div class="wrapper">
+              <div>{{ scope.row.click_uv }}</div>
+            </div>
+          </template>
+        </el-table-column>
       </el-table>
       <Paginate />
     </div>

+ 29 - 54
src/views/officialAccount/components/configPublic.vue

@@ -1,15 +1,15 @@
 <template>
   <el-form :model="formCallback" label-width="120px" ref="form" v-loading="loading" class="pr-4">
-    <el-form-item label="配置公众号" prop="checkPlublics" :rules="[{ required: true, message: '公众号必须选择' }]"
-      label-width="120px">
+    <el-form-item label="配置公众号" prop="wx_auth_ids" :rules="[{ required: true, message: '公众号必须选择' }]" label-width="120px">
       <div class="wrapper">
         <input class="filter-input" v-model="filterPlublic" clearable placeholder="请输入公众号名称"
           @change="filterExistPlublic" />
         <div class="wrapper-inner">
           <el-checkbox v-model="selectAllValue" @change="handleCheckAllChange">全选</el-checkbox>
-          <el-checkbox-group v-model="formCallback.checkPlublics" @change="handleCheckedChange" class="flex flex-col">
-            <el-checkbox :key="sIndex" :label="item" v-for="(item, sIndex) in filteredCities">
-              {{ item.label }}
+          <el-checkbox-group v-model="formCallback.wx_auth_ids" @change="handleCheckedChange" class="flex flex-col">
+            <el-checkbox :key="sIndex" :label="item.id" v-for="(item, sIndex) in filteredCities"
+              :checked="Boolean(item.is_auth)">
+              {{ item.nick_name }}
             </el-checkbox>
           </el-checkbox-group>
         </div>
@@ -25,72 +25,33 @@
 
 <script lang="ts" setup>
 import { Plus, InfoFilled } from '@element-plus/icons-vue';
+import { wechatPlatformKeywordAuthList, wechatPlatformKeywordAllocation } from '@/api/officialAccount/keywordReply/index'
 import { FormInstance } from 'element-plus';
 const props = defineProps({
   primary: Object | null,
 });
+const form = ref<FormInstance>()
 const emit = defineEmits(['close']);
 const loading = ref(false)
-const formCallback = ref({ type: 1 })
+const formCallback = ref({ wx_auth_ids: [] })
 const filterPlublic = ref('')
 // const isIndeterminate = ref(false)
-const goalOptions = [
-  {
-    value: 'Option1',
-    label: 'Option1',
-  },
-  {
-    value: 'Option2',
-    label: 'Option2',
-  },
-  {
-    value: 'Option3',
-    label: 'Option3',
-  },
-  {
-    value: 'Option4',
-    label: 'Option4',
-  },
-  {
-    value: 'Option5',
-    label: 'Option5',
-  },
-  {
-    value: 'Option6',
-    label: 'Option6',
-  },
-  {
-    value: 'Option7',
-    label: 'Option7',
-  },
-  {
-    value: 'Option8',
-    label: 'Option8',
-  },
-  {
-    value: 'Option9',
-    label: 'Option9',
-  },
-  {
-    value: 'Option10',
-    label: 'Option10',
-  },
-]
+let goalOptions = ref([])
 
 const filteredCities = computed(() => {
-  return goalOptions.filter(city => city.label.includes(filterPlublic.value));
+  return goalOptions.value.filter(city => city.nick_name.includes(filterPlublic.value));
 });
 
 const selectAllValue = computed(() => {
-  return formCallback.value?.checkPlublics?.length == goalOptions.length
+  return formCallback.value?.wx_auth_ids?.length > 0 && formCallback.value?.wx_auth_ids?.length == goalOptions.value.length
 });
 
 const filterExistPlublic = () => {
   console.log('filterExistPlublicfilterExistPlublic');
 }
 const handleCheckAllChange = (val: boolean) => {
-  formCallback.value.checkPlublics = val ? filteredCities.value : []
-  console.log(formCallback.value.checkPlublics, val, 'formCallback.value.checkPlublics');
+  formCallback.value.wx_auth_ids = val ? filteredCities.value : []
+  console.log(formCallback.value.wx_auth_ids, val, 'formCallback.value.wx_auth_ids');
   // isIndeterminate.value = false
 }
 const handleCheckedChange = (value: string[]) => {
@@ -105,7 +66,13 @@ const submitForm = (formEl: FormInstance | undefined) => {
   formEl
     .validate(valid => {
       if (valid) {
-
+        console.log(formCallback.value, 'formCallback.valueformCallback.value');
+        const wx_auth_ids = formCallback.value.wx_auth_ids.join(',')
+        wechatPlatformKeywordAllocation(props.primary.id, { wx_auth_ids }).then(res => {
+          console.log(res, 'wx_auth_ids');
+          ElMessage.success(res.message)
+          emit('close')
+        })
         loading.value = false;
       } else {
         loading.value = false;
@@ -116,7 +83,15 @@ const submitForm = (formEl: FormInstance | undefined) => {
 
 
 if (props.primary) {
-  formCallback.value = JSON.parse(JSON.stringify(props.primary))
+  wechatPlatformKeywordAuthList(props.primary.id).then(res => {
+    goalOptions.value = res.data
+    console.log(goalOptions.value, 'goalOptionsgoalOptions');
+    const wx_auth_ids = goalOptions.value?.filter(el => el.is_auth).map(el => el.id)
+    if (wx_auth_ids.length > 0) {
+      formCallback.value.wx_auth_ids = wx_auth_ids
+    }
+  })
+  console.log(props.primary, formCallback.value, 'props.primaryprops.primary');
 }
 onMounted(() => {
 });

+ 105 - 28
src/views/officialAccount/keywordReply/form/create.vue

@@ -1,6 +1,6 @@
 <template>
   <el-form :model="formCallback" label-width="120px" ref="form" v-loading="loading" class="pr-4">
-    <el-form-item label="关键词" prop="is_roi" :rules="[{ required: true, message: '关键词必须填写' }]" label-width="120px">
+    <el-form-item label="关键词" prop="keyword" :rules="[{ required: true, message: '关键词必须填写' }]" label-width="120px">
       <template #label>
         <div class="flex items-center">
           <el-tooltip placement="top">
@@ -14,16 +14,16 @@
           <span>关键词</span>
         </div>
       </template>
-      <el-input v-model="formCallback.key" placeholder="请输入关键词" />
+      <el-input :disabled="props.primary?.look" v-model="formCallback.keyword" placeholder="请输入关键词" />
     </el-form-item>
     <el-form-item label="消息内容" prop="type" :rules="[{ required: true, message: '消息类型必须填写' }]" label-width="120px">
-      <el-radio-group v-model="formCallback.type">
-        <el-radio :label="2">文本消息</el-radio>
+      <el-radio-group v-model="formCallback.type" :disabled="props.primary?.look">
+        <el-radio label="txt">文本消息</el-radio>
       </el-radio-group>
     </el-form-item>
-    <el-form-item label="" label-width="120px">
+    <el-form-item label="" label-width="120px" prop="content" :rules="[{ required: true, message: '消息内容必须插入' }]">
       <el-card class="box-card" style="width:500px;">
-        <template #header>
+        <template #header v-if="!props.primary?.look">
           <div class="card-header">
             <el-popover placement="right" :visible="popoverVisible" trigger="click">
               <template #reference>
@@ -36,10 +36,26 @@
             </el-popover>
           </div>
         </template>
-        <div v-for="o in 4" :key="o" class="text item">{{ 'List item ' + o }}</div>
+        <div class="insert-content">
+          <div v-for="(item, index) in formCallback.content" :key="index">
+            <el-button type="primary" link v-if="item.url" @click="linkClick({ index, ...item })">
+              {{ item.title }}
+            </el-button>
+            <el-input style="boder:none;" :disabled="props.primary?.look" v-else v-model="item.title"
+              clearable></el-input>
+          </div>
+        </div>
       </el-card>
     </el-form-item>
-    <Dialog v-model="insertVisible" :title="insertTitle" destroy-on-close>
+    <div class="flex justify-end" v-if="!props.primary?.look">
+      <el-button type="primary" @click="submitForm(form)">{{
+        $t('system.confirm')
+      }}</el-button>
+    </div>
+  </el-form>
+
+  <Dialog v-model="insertVisible" :title="insertTitle" destroy-on-close @update:modelValue="formCallbackinsert = {}">
+    <el-form :model="formCallbackinsert" label-width="120px" ref="insertform" v-loading="loading" class="pr-4">
       <div v-if="insertType == 'link'">
         <div class="withdraw-popup-warn">
           <span>需先</span>
@@ -47,30 +63,29 @@
             target="_blank">关联小程序</el-link>
           <span>,已关联的小程序可被使用在自定义菜单、模板消息和附近的小程序等场景中</span>
         </div>
-        <el-form-item label="标题" prop="account_id" :rules="[{ required: true, message: '标题必须填写' }]" label-width="120px">
-          <el-input style="width:300px;" v-model="formCallback.account_id" auto-complete="off"
-            placeholder="请输入标题"></el-input>
+        <el-form-item label="标题" prop="title" :rules="[{ required: true, message: '标题必须填写' }]" label-width="120px">
+          <el-input style="width:300px;" :disabled="props.primary?.look" v-model="formCallbackinsert.title"
+            auto-complete="off" placeholder="请输入标题"></el-input>
         </el-form-item>
-        <el-form-item label="链接" prop="account_id" :rules="[{ required: true, message: '链接必须填写' }]" label-width="120px">
-          <el-input style="width:300px;" v-model="formCallback.account_id" auto-complete="off"
-            placeholder="请输入链接"></el-input>
-          <el-button type="primary" link :icon="Plus" @click="linkVisible = true" class="mr-6">插入链接</el-button>
+        <el-form-item label="链接" prop="url" :rules="[{ required: true, message: '链接必须填写' }]" label-width="120px">
+          <el-input style="width:300px;" :disabled="props.primary?.look" v-model="formCallbackinsert.url"
+            auto-complete="off" placeholder="请输入链接"></el-input>
+          <el-button v-if="!props.primary?.look" type="primary" link :icon="Plus" @click="linkVisible = true"
+            class="mr-6">插入链接</el-button>
         </el-form-item>
       </div>
       <div v-else>
-        <el-form-item label="标题" prop="account_id" :rules="[{ required: true, message: '标题必须填写' }]" label-width="120px">
-          <el-input style="width:300px;" type="textarea" v-model="formCallback.account_id" auto-complete="off"
-            placeholder="请输入标题"></el-input>
+        <el-form-item label="标题" prop="title" :rules="[{ required: true, message: '标题必须填写' }]" label-width="120px">
+          <el-input style="width:300px;" :disabled="props.primary?.look" type="textarea"
+            v-model="formCallbackinsert.title" auto-complete="off" placeholder="请输入标题"></el-input>
         </el-form-item>
       </div>
-    </Dialog>
+      <div class="flex justify-end" v-if="!props.primary?.look">
+        <el-button type="primary" @click="insertSubmitChange(insertform)">确定</el-button>
+      </div>
+    </el-form>
+  </Dialog>
 
-    <div class="flex justify-end">
-      <el-button type="primary" @click="submitForm(form)">{{
-        $t('system.confirm')
-      }}</el-button>
-    </div>
-  </el-form>
   <Dialog v-model="linkVisible" title="选择链接" destroy-on-close>
     <generateLink @close="linkClose" />
   </Dialog>
@@ -80,9 +95,13 @@
 import { Plus, InfoFilled } from '@element-plus/icons-vue';
 import { FormInstance } from 'element-plus';
 import { useRouter, useRoute } from 'vue-router'
+import { wechatPlatformKeywordAdd, wechatPlatformKeywordEdit } from '@/api/officialAccount/keywordReply/index'
 import generateLink from './generateLink.vue'
+import Cache from '@/support/cache';
 const router = useRouter()
 const route = useRoute()
+const insertform = ref()
+const form = ref()
 const props = defineProps({
   primary: Object | null,
 });
@@ -91,7 +110,8 @@ const loading = ref(false)
 const linkVisible = ref(false)
 const popoverVisible = ref(false)
 const insertVisible = ref(false)
-const formCallback = ref({ type: 1 })
+const formCallback = ref({ type: 'txt', content: [] })
+const formCallbackinsert = ref({})
 const insertType = ref()
 const insertTitle = computed(() => insertType.value == 'link' ? '插入小程序链接' : '插入纯文本')
 const insertChange = (type: string) => {
@@ -99,7 +119,29 @@ const insertChange = (type: string) => {
   insertVisible.value = true;
   popoverVisible.value = false;
 }
-
+const linkClick = (row: object) => {
+  formCallbackinsert.value = row
+  insertVisible.value = true
+  insertType.value = 'link'
+}
+const insertSubmitChange = (formEl: FormInstance | undefined) => {
+  if (!formEl) return;
+  if (!formEl) return;
+  formEl
+    .validate(valid => {
+      if (valid) {
+        if (formCallbackinsert.value.index != undefined) {
+          formCallback.value.content[formCallbackinsert.value?.index] = formCallbackinsert.value
+        } else {
+          formCallback.value.content?.push(formCallbackinsert.value)
+        }
+        insertVisible.value = false
+        formCallbackinsert.value = {}
+      } else {
+      }
+    })
+    .then(() => { });
+}
 
 const linkClose = (e: any) => {
   console.log(e);
@@ -112,7 +154,28 @@ const submitForm = (formEl: FormInstance | undefined) => {
   formEl
     .validate(valid => {
       if (valid) {
-
+        let params = {
+          type: formCallback.value.type,
+          keyword: formCallback.value.keyword,
+          miniprogram_id: formCallback.value.miniprogram_id,
+          content: formCallback.value.content,
+        }
+        if (props.primary?.id) {
+          params.id = props.primary?.id
+          wechatPlatformKeywordEdit(props.primary?.id, params).then(res => {
+            ElMessage.success(res.message)
+            emit('close')
+          }).catch(e => {
+            loading.value = false;
+          })
+        } else {
+          wechatPlatformKeywordAdd(params).then(res => {
+            ElMessage.success(res.message)
+            emit('close')
+          }).catch(e => {
+            loading.value = false;
+          })
+        }
         loading.value = false;
       } else {
         loading.value = false;
@@ -123,9 +186,13 @@ const submitForm = (formEl: FormInstance | undefined) => {
 
 
 if (props.primary) {
+  console.log(props.primary, 'props.primary');
   formCallback.value = JSON.parse(JSON.stringify(props.primary))
 }
 onMounted(() => {
+  if (JSON.parse(Cache.get('nav_data'))?.app.id) {
+    formCallback.value.miniprogram_id = JSON.parse(Cache.get('nav_data'))?.app.id
+  }
 });
 </script>
 
@@ -136,4 +203,14 @@ onMounted(() => {
   color: #666;
   margin-bottom: 12px;
 }
+
+.insert-content {
+  min-height: 200px;
+  max-height: 468px;
+  overflow: hidden;
+  overflow-y: auto;
+  line-height: 20px;
+  height: 20px;
+  padding: 10px;
+}
 </style>

+ 27 - 24
src/views/officialAccount/keywordReply/form/generateLink.vue

@@ -1,28 +1,27 @@
 <template>
-  <el-form :model="formCallback" label-width="120px" ref="form" v-loading="loading" class="pr-4">
-    <el-form-item label="消息类型" prop="roi" :rules="[{ required: true, message: '消息类型必须填写' }]" label-width="120px">
-      <el-radio-group v-model="formCallback.roi">
-        <el-radio :label="1">视频链接</el-radio>
-      </el-radio-group>
-    </el-form-item>
-    <el-form-item label="短剧" prop="video_id">
-      <el-select v-model="formCallback.video_id" class="w-full" clearable filterable remote :remote-method="remoteMethod"
-        placeholder="请输入短剧">
-        <el-option v-for="item in videoList" :key="item.id" :label="item.name" :value="item.id" />
-      </el-select>
-    </el-form-item>
-    <el-form-item label="剧集" prop="video_id">
-      <el-select v-model="formCallback.video_id" class="w-full" clearable filterable remote :remote-method="remoteMethod"
-        placeholder="请选择剧集">
-        <el-option v-for="item in videoList" :key="item.id" :label="item.name" :value="item.id" />
-      </el-select>
-    </el-form-item>
-    <div class="flex justify-end">
-      <el-button type="primary" @click="submitForm(form)">{{
-        $t('system.confirm')
-      }}</el-button>
-    </div>
-  </el-form>
+  <el-tabs>
+    <el-tab-pane label="视频链接">
+      <el-form :model="formCallback" label-width="120px" ref="form" v-loading="loading" class="pr-4">
+        <el-form-item label="短剧" prop="video_id">
+          <el-select v-model="formCallback.video_id" class="w-full" clearable filterable remote
+            :remote-method="remoteMethod" placeholder="请输入短剧">
+            <el-option v-for="item in videoList" :key="item.id" :label="item.name" :value="item.id" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="剧集" prop="video_id">
+          <el-select v-model="formCallback.video_id" class="w-full" clearable filterable remote
+            :remote-method="remoteMethod" placeholder="请选择剧集">
+            <el-option v-for="item in videoList" :key="item.id" :label="item.name" :value="item.id" />
+          </el-select>
+        </el-form-item>
+        <div class="flex justify-end">
+          <el-button type="primary" @click="submitForm(form)">{{
+            $t('system.confirm')
+          }}</el-button>
+        </div>
+      </el-form>
+    </el-tab-pane>
+  </el-tabs>
 </template>
 
 <script lang="ts" setup>
@@ -43,6 +42,10 @@ const initVideoList = (params?: object) => {
     videoList.value = res.data
   })
 }
+
+const initVideoStockEpisodeList = () => {
+
+}
 const remoteMethod = (query: string) => {
   if (query) {
     initVideoList({ videoName: query })

+ 41 - 12
src/views/officialAccount/keywordReply/index.vue

@@ -37,9 +37,9 @@
             </div>
             <el-table :data="tableData" class="mt-3" v-loading="loading" @selection-change="handleSelectionChange">
               <el-table-column type="selection" width="80"></el-table-column>
-              <el-table-column prop="name" label="关键字" min-width="200"></el-table-column>
-              <el-table-column prop="name" label="创建时间" min-width="200"></el-table-column>
-              <el-table-column prop="name" label="总发送次数" min-width="200">
+              <el-table-column prop="keyword" label="关键字" min-width="200"></el-table-column>
+              <el-table-column prop="created_at" label="创建时间" min-width="200"></el-table-column>
+              <el-table-column prop="send_total" label="总发送次数" min-width="200">
                 <template #header>
                   <div class="flex items-center">
                     <span>总发送次数</span>
@@ -52,15 +52,16 @@
                   </div>
                 </template>
                 <template #default="scope">
-                  <span>{{ scope.row.updated_at }}</span>
+                  <span>{{ scope.row.send_total }}</span>
                 </template>
               </el-table-column>
-              <el-table-column prop="name" label="配置公众号" min-width="200">
+              <el-table-column prop="nick_name" label="配置公众号" min-width="200">
                 <template #default="scope">
                   <div class="flex flex-col">
-                    <div class="text-lg font-bold text-blue-400 cursor-pointer"
-                      @click="openType('createVisible', { single: true, ...scope.row })">
-                      {{ scope.row.name }}
+                    <!-- text-blue-400 cursor-pointer -->
+                    <div class="text-lg font-bold " v-for="item in scope.row.wechat_accounts" :key="item.id">
+                      <!--  @click="openType('createVisible', { single: true, currentwechat: item, ...scope.row })" -->
+                      {{ item.nick_name }}
                     </div>
                   </div>
                 </template>
@@ -88,7 +89,7 @@
         <Create @close="closeType('createVisible')" :primary="current" />
       </Dialog>
       <Dialog v-model="configPublicVisible" title="配置公众号" destroy-on-close>
-        <configPublic @close="closeType('createVisible')" :primary="current" />
+        <configPublic @close="closeType('configPublicVisible')" :primary="current" />
       </Dialog>
     </div>
   </div>
@@ -100,7 +101,13 @@ import Create from './form/create.vue';
 import configPublic from '@/views/officialAccount/components/configPublic.vue'
 import { useGetList } from '@/hook/curd/useGetList';
 import { manageMiniprogramCompanylist, manageMiniprogramTypelist } from '@/api/applet/index'
-const api = 'manage/miniprogram/index';
+import {
+  wechatPlatformKeywordGetConfig,
+  wechatPlatformKeywordGetConfigSet,
+  wechatPlatformKeywordDel
+} from '@/api/officialAccount/keywordReply'
+const api = 'wechatPlatform/keyword/list';
+import Cache from '@/support/cache';
 const createVisible = ref(false)
 const configPublicVisible = ref(false)
 const isEnableReply = ref(true)
@@ -125,7 +132,9 @@ const openType = (type: string, data: object | null) => {
       if (current.value?.id) {
         createTitle.value = '编辑'
         if (current.value.single) {
-          createTitle.value = current.value.name
+          createTitle.value = current.value.currentwechat.nick_name
+        } else if (current.value.look) {
+          createTitle.value = "查看"
         }
       } else {
         createTitle.value = '新增'
@@ -153,7 +162,12 @@ const mulSet = () => {
       }
     )
       .then(() => {
-
+        const ids = multipleSelection.value.map(el => el.id).join(',')
+        wechatPlatformKeywordDel({ ids }).then(res => {
+          console.log(res);
+          ElMessage.success(res.message)
+          search()
+        })
       })
       .catch(() => {
 
@@ -166,6 +180,9 @@ const closeType = (type: string) => {
     case 'createVisible':
       createVisible.value = false
       break;
+    case 'configPublicVisible':
+      configPublicVisible.value = false
+      break;
   }
   search()
 }
@@ -181,6 +198,11 @@ const deleteChange = (row: object) => {
     }
   )
     .then(() => {
+      wechatPlatformKeywordDel({ ids: row.id }).then(res => {
+        console.log(res);
+        ElMessage.success(res.message)
+        search()
+      })
       // tableData.value.splice(index, 1)
     })
     .catch(() => {
@@ -197,6 +219,13 @@ const init = () => {
 }
 
 onMounted(() => {
+  if (JSON.parse(Cache.get('nav_data'))?.app.id) {
+    const miniprogram_id = JSON.parse(Cache.get('nav_data'))?.app.id
+    wechatPlatformKeywordGetConfig(miniprogram_id).then(res => {
+      console.log(res, 'wechatPlatformKeywordGetConfig');
+    })
+  }
+
   init()
   search();
 });

+ 249 - 0
src/views/officialAccount/newsService/form/create.vue

@@ -0,0 +1,249 @@
+<template>
+  <el-form :model="formCallback" label-width="120px" ref="form" v-loading="loading" class="pr-4">
+    <el-form-item label="活动名称" prop="is_roi" :rules="[{ required: true, message: '活动名称必须填写' }]" label-width="120px">
+      <el-input v-model="formCallback.keyword" placeholder="请输入活动名称" />
+    </el-form-item>
+    <el-form-item label="消息内容" prop="type" :rules="[{ required: true, message: '消息类型必须填写' }]" label-width="120px">
+      <el-radio-group v-model="formCallback.type">
+        <el-radio label="txt">文本消息</el-radio>
+      </el-radio-group>
+    </el-form-item>
+    <el-form-item label="" label-width="120px">
+      <el-card class="box-card" style="width:500px;">
+        <template #header>
+          <div class="card-header">
+            <el-popover placement="right" :visible="popoverVisible" trigger="click">
+              <template #reference>
+                <el-button link :icon="Plus" @click="popoverVisible = !popoverVisible">插入内容</el-button>
+              </template>
+              <div class="flex flex-col items-start justify-start">
+                <el-link :underline="false" class="m-1" @click="insertChange('link')">插入小程序链接</el-link>
+                <el-link :underline="false" class="m-1" @click="insertChange('text')">插入纯文本</el-link>
+              </div>
+            </el-popover>
+          </div>
+        </template>
+        <div class="insert-content">
+          <div v-for="(item, index) in formCallback.content" :key="index">
+            <el-button type="primary" link v-if="item.link" @click="linkClick({ index, ...item })">
+              {{ item.title }}
+            </el-button>
+            <el-input style="boder:none;" v-else v-model="item.title" clearable></el-input>
+          </div>
+        </div>
+      </el-card>
+    </el-form-item>
+    <el-form-item label="发送用户" prop="is_roi" :rules="[{ required: true, message: '活动名称必须填写' }]" label-width="120px">
+      <el-radio-group v-model="formCallback.user">
+        <el-radio :label="3">全部粉丝</el-radio>
+        <el-radio :label="6">标签用户</el-radio>
+      </el-radio-group>
+    </el-form-item>
+    <el-form-item label="人群包" prop="callback_config_id"
+      :rules="[{ required: true, message: '请选择人群包', trigger: 'change' }]">
+      <el-select v-model="formCallback.callback_config_id" filterable remote clearable :remote-method="remoteMethod"
+        placeholder="请选择人群包">
+        <el-option v-for="(item, index) in crowdPackageList" :key="index" :label="item.name" :value="item.id" />
+      </el-select>
+      <el-button type="primary" link size="default" @click="addCrowdPackage">新增人群包</el-button>
+    </el-form-item>
+    <el-form-item label="发送时间" prop="callback_config_id"
+      :rules="[{ required: true, message: '请选择发送时间', trigger: 'change' }]">
+      <div>
+        <el-date-picker unlink-panels clearable @change="sendTimeChange" format="YYYY/MM/DD hh:mm:ss"
+          value-format="YYYY-MM-DD h:m:s" v-model="formCallback.sendTime" type="datetime" placeholder="选择日期时间" />
+      </div>
+      <div>
+        <el-button type="primary" v-for="(time, index) in timeArr" :key="index" link size="default"
+          @click="setSendTime(time)">{{ time.name }}</el-button>
+      </div>
+    </el-form-item>
+    <div class="flex justify-end">
+      <el-button type="primary" @click="submitForm(form)">确定</el-button>
+    </div>
+  </el-form>
+
+  <Dialog v-model="insertVisible" :title="insertTitle" destroy-on-close @update:modelValue="formCallbackinsert = {}">
+    <el-form :model="formCallbackinsert" label-width="120px" ref="insertform" v-loading="loading" class="pr-4">
+      <div v-if="insertType == 'link'">
+        <div class="withdraw-popup-warn">
+          <span>需先</span>
+          <el-link class="text-lg font-bold text-blue-400 cursor-pointer" type="primary" href="https://element-plus.org"
+            target="_blank">关联小程序</el-link>
+          <span>,已关联的小程序可被使用在自定义菜单、模板消息和附近的小程序等场景中</span>
+        </div>
+        <el-form-item label="标题" prop="title" :rules="[{ required: true, message: '标题必须填写' }]" label-width="120px">
+          <el-input style="width:300px;" v-model="formCallbackinsert.title" auto-complete="off"
+            placeholder="请输入标题"></el-input>
+        </el-form-item>
+        <el-form-item label="链接" prop="link" :rules="[{ required: true, message: '链接必须填写' }]" label-width="120px">
+          <el-input style="width:300px;" v-model="formCallbackinsert.link" auto-complete="off"
+            placeholder="请输入链接"></el-input>
+          <el-button type="primary" link :icon="Plus" @click="linkVisible = true" class="mr-6">插入链接</el-button>
+        </el-form-item>
+      </div>
+      <div v-else>
+        <el-form-item label="标题" prop="title" :rules="[{ required: true, message: '标题必须填写' }]" label-width="120px">
+          <el-input style="width:300px;" type="textarea" v-model="formCallbackinsert.title" auto-complete="off"
+            placeholder="请输入标题"></el-input>
+        </el-form-item>
+      </div>
+      <div class="flex justify-end">
+        <el-button type="primary" @click="insertSubmitChange(insertform)">确定</el-button>
+      </div>
+    </el-form>
+  </Dialog>
+
+  <Dialog v-model="linkVisible" title="选择链接" destroy-on-close>
+    <generateLink @close="linkClose" />
+  </Dialog>
+</template>
+
+<script lang="ts" setup>
+import { Plus, InfoFilled } from '@element-plus/icons-vue';
+import { FormInstance } from 'element-plus';
+import { useRouter, useRoute } from 'vue-router'
+import { wechatPlatformKeywordAdd, wechatPlatformKeywordEdit } from '@/api/officialAccount/keywordReply/index'
+import generateLink from './generateLink.vue'
+import moment from 'moment';
+const router = useRouter()
+const route = useRoute()
+const insertform = ref()
+const form = ref()
+const crowdPackageList = ref([])
+const props = defineProps({
+  primary: Object | null,
+});
+const timeArr = [
+  { name: '10分钟后', value: '10', format: "minutes" },
+  { name: '30分钟后', value: '30', format: "minutes" },
+  { name: '1小时后', value: '1', format: "hours" }
+]
+const emit = defineEmits(['close']);
+const loading = ref(false)
+const linkVisible = ref(false)
+const popoverVisible = ref(false)
+const insertVisible = ref(false)
+const formCallback = ref({ type: 'txt', content: [] })
+const formCallbackinsert = ref({})
+const insertType = ref()
+const insertTitle = computed(() => insertType.value == 'link' ? '插入小程序链接' : '插入纯文本')
+const insertChange = (type: string) => {
+  insertType.value = type;
+  insertVisible.value = true;
+  popoverVisible.value = false;
+}
+const linkClick = (row: object) => {
+  formCallbackinsert.value = row
+  insertVisible.value = true
+  insertType.value = 'link'
+}
+
+const setSendTime = (item: object) => {
+  console.log(item, 'setSendTime', moment().add(item.value, item.format).format('YYYY-MM-DD HH:mm:ss'));
+  formCallback.value.sendTime = moment().add(item.value, item.format).format('YYYY-MM-DD HH:mm:ss')
+}
+const sendTimeChange = (e: object) => {
+  console.log(e, 'timeChangetimeChangetimeChange');
+}
+
+const addCrowdPackage = () => {
+  router.push({ path: '', query: {} })
+}
+
+const initRemoteOption = (type: string, params?: object) => {
+  switch (type) {
+    case '':
+      break;
+  }
+}
+
+const remoteMethod = (query: string) => {
+  if (query) {
+    initRemoteOption("callbackJuliangAccountList", { name: query })
+  } else {
+    initRemoteOption("callbackJuliangAccountList")
+  }
+}
+
+const insertSubmitChange = (formEl: FormInstance | undefined) => {
+  if (!formEl) return;
+  if (!formEl) return;
+  formEl
+    .validate(valid => {
+      if (valid) {
+        if (formCallbackinsert.value.index != undefined) {
+          formCallback.value.content[formCallbackinsert.value?.index] = formCallbackinsert.value
+        } else {
+          formCallback.value.content.push(formCallbackinsert.value)
+        }
+        insertVisible.value = false
+        formCallbackinsert.value = {}
+      } else {
+      }
+    })
+    .then(() => { });
+}
+
+const linkClose = (e: any) => {
+  console.log(e);
+}
+// 提交回传配置
+const submitForm = (formEl: FormInstance | undefined) => {
+  if (!formEl) return;
+  loading.value = true;
+  if (!formEl) return;
+  formEl
+    .validate(valid => {
+      if (valid) {
+        if (props.primary?.id) {
+          wechatPlatformKeywordEdit(props.primary?.id, formCallback.value).then(res => {
+            ElMessage.success(res.message)
+            emit('close')
+          }).catch(e => {
+            loading.value = false;
+          })
+        } else {
+          wechatPlatformKeywordAdd(formCallback.value).then(res => {
+            ElMessage.success(res.message)
+            emit('close')
+          }).catch(e => {
+            loading.value = false;
+          })
+        }
+        loading.value = false;
+      } else {
+        loading.value = false;
+      }
+    })
+    .then(() => { });
+}
+
+
+if (props.primary) {
+  console.log(props.primary, 'props.primary');
+  formCallback.value = JSON.parse(JSON.stringify(props.primary))
+  formCallback.value.content = [{ title: 'dsds' }, { title: 'sdds', link: 'dsdsds' }]
+}
+onMounted(() => {
+});
+</script>
+
+<style lang="scss" scoped>
+.withdraw-popup-warn {
+  font-size: 14px;
+  font-weight: 600;
+  color: #666;
+  margin-bottom: 12px;
+}
+
+.insert-content {
+  min-height: 200px;
+  max-height: 468px;
+  overflow: hidden;
+  overflow-y: auto;
+  line-height: 20px;
+  height: 20px;
+  padding: 10px;
+}
+</style>

+ 102 - 0
src/views/officialAccount/newsService/form/generateLink.vue

@@ -0,0 +1,102 @@
+<template>
+  <el-form :model="formCallback" label-width="120px" ref="form" v-loading="loading" class="pr-4">
+    <el-form-item label="消息类型" prop="roi" :rules="[{ required: true, message: '消息类型必须填写' }]" label-width="120px">
+      <el-radio-group v-model="formCallback.roi">
+        <el-radio :label="1">视频链接</el-radio>
+      </el-radio-group>
+    </el-form-item>
+    <el-form-item label="短剧" prop="video_id">
+      <el-select v-model="formCallback.video_id" class="w-full" clearable filterable remote :remote-method="remoteMethod"
+        placeholder="请输入短剧">
+        <el-option v-for="item in videoList" :key="item.id" :label="item.name" :value="item.id" />
+      </el-select>
+    </el-form-item>
+    <el-form-item label="剧集" prop="video_id">
+      <el-select v-model="formCallback.video_id" class="w-full" clearable filterable remote :remote-method="remoteMethod"
+        placeholder="请选择剧集">
+        <el-option v-for="item in videoList" :key="item.id" :label="item.name" :value="item.id" />
+      </el-select>
+    </el-form-item>
+    <div class="flex justify-end">
+      <el-button type="primary" @click="submitForm(form)">{{
+        $t('system.confirm')
+      }}</el-button>
+    </div>
+  </el-form>
+</template>
+
+<script lang="ts" setup>
+import { Plus, InfoFilled } from '@element-plus/icons-vue';
+import { FormInstance } from 'element-plus';
+import { videoStockVideoList } from '@/api/video/index'
+const props = defineProps({
+  primary: Object | null,
+});
+const emit = defineEmits(['close']);
+const loading = ref(false)
+const formCallback = ref({})
+const videoList = ref([])
+
+const initVideoList = (params?: object) => {
+  videoStockVideoList({ limit: 999, ...params }).then(res => {
+    console.log(res);
+    videoList.value = res.data
+  })
+}
+const remoteMethod = (query: string) => {
+  if (query) {
+    initVideoList({ videoName: query })
+  } else {
+    initVideoList()
+  }
+}
+
+// 提交回传配置
+const submitForm = (formEl: FormInstance | undefined) => {
+  if (!formEl) return;
+  loading.value = true;
+  if (!formEl) return;
+  formEl
+    .validate(valid => {
+      if (valid) {
+
+        loading.value = false;
+      } else {
+        loading.value = false;
+      }
+    })
+    .then(() => { });
+}
+
+
+if (props.primary) {
+  formCallback.value = JSON.parse(JSON.stringify(props.primary))
+}
+onMounted(() => {
+  initVideoList()
+});
+</script>
+
+<style lang="scss" scoped>
+.wrapper {
+  width: 280px;
+  border: 1px solid rgba($color: #dcdfe6, $alpha: 1);
+  border-top: none;
+  border-radius: 6px;
+
+  .filter-input {
+    width: 100%;
+    border-radius: 6px;
+    border: 1px solid #dcdfe6;
+    outline: none;
+    padding: 0 6px;
+  }
+
+  .wrapper-inner {
+    height: 300px;
+    overflow: auto;
+
+    padding: 9px;
+  }
+}
+</style>

+ 199 - 0
src/views/officialAccount/newsService/index.vue

@@ -0,0 +1,199 @@
+<template>
+  <div class="flex flex-col justify-between w-full sm:flex-row">
+    <div class="w-full">
+      <Search :search="search" :reset="reset">
+        <template v-slot:body>
+
+          <el-form-item label="关键字">
+            <el-input placeholder="请输入关键字" v-model="query.key" clearable></el-input>
+          </el-form-item>
+          <el-form-item label="公众号">
+            <el-select v-model="query.type" clearable filterable placeholder="请选择">
+              <el-option v-for="(item, index) in officialAccountsList" :key="index" :label="item.name"
+                :value="item.value" />
+            </el-select>
+          </el-form-item>
+        </template>
+        <template v-slot:content>
+          <div class="mt-3">
+            <el-alert title="仅48小时内和公众号有互动的粉丝才能收到,最多发送5条" type="warning" show-icon :closable="false" />
+          </div>
+          <div class="table-default">
+            <div class="pt-5 pl-2">
+              <el-button type="primary" size="default" @click="openType('createVisible', null)">新增</el-button>
+              <el-button size="default" @click="mulSet">批量删除</el-button>
+            </div>
+            <el-table :data="tableData" class="mt-3" v-loading="loading" @selection-change="handleSelectionChange">
+              <el-table-column type="selection" width="80"></el-table-column>
+              <el-table-column prop="name" label="关键字" min-width="200"></el-table-column>
+              <el-table-column prop="name" label="创建时间" min-width="200"></el-table-column>
+              <el-table-column prop="name" label="总发送次数" min-width="200">
+                <template #header>
+                  <div class="flex items-center">
+                    <span>总发送次数</span>
+                    <el-tooltip placement="top">
+                      <template #content> 统计该关键字及内容在所有已配置公众号中的累计发送次数<br /></template>
+                      <el-icon>
+                        <InfoFilled />
+                      </el-icon>
+                    </el-tooltip>
+                  </div>
+                </template>
+                <template #default="scope">
+                  <span>{{ scope.row.updated_at }}</span>
+                </template>
+              </el-table-column>
+              <el-table-column prop="name" label="配置公众号" min-width="200">
+                <template #default="scope">
+                  <div class="flex flex-col">
+                    <div class="text-lg font-bold text-blue-400 cursor-pointer"
+                      @click="openType('createVisible', { single: true, ...scope.row })">
+                      {{ scope.row.name }}
+                    </div>
+                  </div>
+                </template>
+              </el-table-column>
+              <el-table-column label="操作" width="200" fixed="right">
+                <template #default="scope">
+                  <el-button link type="primary" size="small" @click="openType('createVisible', scope.row)">编辑</el-button>
+                  <br />
+                  <el-button link type="primary" size="small"
+                    @click="openType('createVisible', { look: true, ...scope.row })">查看</el-button>
+                  <br />
+                  <el-button link type="primary" size="small" @click="deleteChange(scope.row)">删除</el-button>
+                  <br />
+                  <el-button link type="primary" size="small"
+                    @click="openType('configPublicVisible', scope.row)">配置公众号</el-button>
+                  <br />
+                </template>
+              </el-table-column>
+            </el-table>
+            <Paginate />
+          </div>
+        </template>
+      </Search>
+      <Dialog v-model="createVisible" :title="createTitle" destroy-on-close>
+        <Create @close="closeType('createVisible')" :primary="current" />
+      </Dialog>
+      <Dialog v-model="configPublicVisible" title="配置公众号" destroy-on-close>
+        <configPublic @close="closeType('createVisible')" :primary="current" />
+      </Dialog>
+    </div>
+  </div>
+</template>
+
+<script lang="ts" setup>
+import { InfoFilled } from '@element-plus/icons-vue';
+import Create from './form/create.vue';
+import configPublic from '@/views/officialAccount/components/configPublic.vue'
+import { useGetList } from '@/hook/curd/useGetList';
+import { manageMiniprogramCompanylist, manageMiniprogramTypelist } from '@/api/applet/index'
+const api = 'wechatPlatform/keyword/list';
+const createVisible = ref(false)
+const configPublicVisible = ref(false)
+const isEnableReply = ref(true)
+const current = ref<object | null>({})
+const messageType = ref([])
+const officialAccountsList = ref([])
+const createTitle = ref('新增')
+const { data, query, search, reset, loading } = useGetList(api);
+const rolesIdentify = inject('rolesIdentify')
+const tableData = computed(() => data.value?.data);
+
+const multipleSelection = ref([])
+// 全选
+const handleSelectionChange = (val) => {
+  multipleSelection.value = val;
+}
+const openType = (type: string, data: object | null) => {
+  current.value = data;
+  switch (type) {
+    case 'createVisible':
+      createVisible.value = true
+      if (current.value?.id) {
+        createTitle.value = '编辑'
+        if (current.value.single) {
+          createTitle.value = current.value.name
+        }
+      } else {
+        createTitle.value = '新增'
+      }
+      break;
+    case 'configPublicVisible':
+      configPublicVisible.value = true
+      break;
+  }
+}
+
+// 批量删除
+const mulSet = () => {
+  if (multipleSelection.value.length <= 0) {
+    return ElMessage.warning('至少选择一条数据');
+  } else {
+    const content = '确定批量删除吗?'
+    ElMessageBox.confirm(
+      content,
+      '提示',
+      {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning',
+      }
+    )
+      .then(() => {
+
+      })
+      .catch(() => {
+
+      })
+  }
+}
+
+const closeType = (type: string) => {
+  switch (type) {
+    case 'createVisible':
+      createVisible.value = false
+      break;
+  }
+  search()
+}
+
+const deleteChange = (row: object) => {
+  ElMessageBox.confirm(
+    '确定要删除吗?',
+    '提示',
+    {
+      confirmButtonText: '确定',
+      cancelButtonText: '取消',
+      type: 'warning',
+    }
+  )
+    .then(() => {
+      // tableData.value.splice(index, 1)
+    })
+    .catch(() => {
+
+    })
+}
+const init = () => {
+  manageMiniprogramCompanylist().then(res => {
+    messageType.value = res.data
+  })
+  manageMiniprogramTypelist().then(res => {
+    officialAccountsList.value = res.data
+  })
+}
+
+onMounted(() => {
+  init()
+  search();
+});
+</script>
+
+<style lang="scss" scoped>
+.withdraw-popup-warn {
+  font-size: 14px;
+  font-weight: 600;
+  color: #666;
+}
+</style>

+ 41 - 0
src/views/officialAccount/officialList/index.vue

@@ -0,0 +1,41 @@
+<template>
+  <div class="flex flex-col justify-between w-full sm:flex-row">
+    <div class="w-full">
+      <Search :search="search" :reset="reset">
+        <template v-slot:body>
+          <el-form-item label="公众号名称">
+            <el-input v-model="query.nick_name" size="default" placeholder="请输入公众号名称" clearable></el-input>
+          </el-form-item>
+        </template>
+      </Search>
+      <div class="table-default">
+        <el-table :data="tableData" class="mt-3" v-loading="loading">
+          <el-table-column label="公众号名称" prop="nick_name">
+          </el-table-column>
+          <el-table-column label="优化师" v-if="rolesIdentify.includes('company')" prop="username">
+          </el-table-column>
+          <el-table-column label="已关联小程序" prop="xcx_name">
+          </el-table-column>
+          <el-table-column label="appid" prop="authorizer_appid">
+          </el-table-column>
+          <el-table-column label="粉丝数" prop="fans_count">
+          </el-table-column>
+        </el-table>
+        <Paginate />
+      </div>
+    </div>
+  </div>
+</template>
+
+<script lang="ts"  setup>
+import { InfoFilled } from '@element-plus/icons-vue';
+import { useGetList } from '@/hook/curd/useGetList';
+const rolesIdentify = inject('rolesIdentify')
+const api = 'wechatPlatform/officialAccount/list';
+const { data, query, search, reset, loading } = useGetList(api, true);
+const tableData = computed(() => data.value?.data);
+onMounted(() => {
+  search();
+});
+</script>
+<style  lang='scss' scoped></style>

+ 0 - 122
src/views/promotion/ranse/index.vue

@@ -1,122 +0,0 @@
-<template>
-	<div class="page">
-		<div class="header">
-			<el-text class="text-notice">注意事项</el-text>
-			<el-text class="text-notice">1.充值用户 >=n 分钟连续未登录,如果点击新连接,会归属于新链接的优化师</el-text>
-			<el-text class="text-notice">2.未充值用户 >=n 分钟连续未登录,如果点击新连接,会归属于新链接的优化师</el-text>
-		</div>
-		
-		<div class="set-box" v-show="init_finish" >
-			<view class="set-box-item">
-				<el-text class="lab-txt"> <label style="color: red;margin-right: 10px;">*</label>非充值用户:</el-text>
-				<el-input-number v-model="no_charge_user_duration" :min="0"  @change="handleUnChargeUserChange" />
-				<el-text style="margin-left: 10px;color: 555;">分</el-text>
-			</view>
-			<view class="set-box-item">
-				<el-text class="lab-txt"> <label style="color: red;margin-right: 10px;">*</label>充值用户:</el-text>
-				<el-input-number v-model="charge_user_duration" :min="0"  @change="handleChargeUserChange" />
-				<el-text style="margin-left: 10px;color: 555;">分</el-text>
-			</view>
-			<view class="set-box-item"  v-action="'tuiguang.RanseConfig.setRanseDuration'" >
-				<el-button :disabled="permission" style="margin-left: 140px;margin-top: 30px;" type="primary" @click="saveSetting">确认</el-button>
-			</view>
-			
-		</div>
-	</div>
-</template>
-
-<script lang="ts" setup>
-	import {getRanseDuration,setRanseDuration} from "@/api/promotion/ranse";
-	const rolesIdentify = inject('rolesIdentify');
-	const setData = ref({charge_user_duration:0,no_charge_user_duration:0})
-	const  charge_user_duration = ref(0);
-	const  no_charge_user_duration = ref(0);
-	const  init_finish = ref(0);
-	const permission = ref(true);
-	
-	const handleUnChargeUserChange = (value: number) => {
-		no_charge_user_duration.value = value;
-	}
-	const handleChargeUserChange = (value: number) => {
-		charge_user_duration.value = value;
-	}
-
-	const saveSetting =  ()  => {
-		if(charge_user_duration.value < 1){
-			ElMessage.error( '请填写充值用户染色时间');
-			return false;
-		}
-		if(no_charge_user_duration.value < 1){
-			ElMessage.error( '请填写未充值用户染色时间');
-			return false;
-		}
-			
-		if(charge_user_duration.value <= no_charge_user_duration.value){
-			ElMessage.error( '充值用户染色时间应大于非充值用户染色时间');
-			return false;
-		}
-		if(setData.value.charge_user_duration == charge_user_duration.value && setData.value.no_charge_user_duration == no_charge_user_duration.value){
-			ElMessage.success("操作成功")
-			return true;
-		}
-		setRanseDuration({charge_user_duration:charge_user_duration.value,no_charge_user_duration:no_charge_user_duration.value}).then(res => {
-			if(res.code ==10000){
-				ElMessage.success(res.message);
-				getSetData();
-			}
-		})
-	}
-	
-	const getSetData = async () =>{
-		if(rolesIdentify.value.includes('optimizer')){
-			await getRanseDuration().then(res => {
-				if(res.code ==10000){
-					setData.value.charge_user_duration = res.data.charge_user_duration;
-					setData.value.no_charge_user_duration = res.data.no_charge_user_duration;
-					charge_user_duration.value = res.data.charge_user_duration;
-					no_charge_user_duration.value = res.data.no_charge_user_duration;
-					init_finish.value = 1;
-				}
-				permission.value = false;
-			});
-		}
-		init_finish.value = 1;
-		
-		
-	}
-	onMounted(async () =>{
-		await getSetData();
-	});
-</script>
-
-<style lang="scss" scoped>
-.lab-txt{
-	display: inline-table;
-	width: 120px;
-	color: #555;
-	text-align: right;
-}
-.page{
-	background-color: #fff;
-	padding:40px 60px;
-}
-.header{
-	display: flex;
-	flex-direction: column;
-	background-color:#fcd3d3 ;
-	padding: 20px 30px;
-}
-.text-notice{
-	width: 100%;
-	color:#f56c6c;
-}
-.set-box{
-	margin-top: 20px;
-	display: flex;
-	flex-direction: column;
-}
-.set-box-item{
-	width: 100%;
-	margin-top: 20px;
-}
-</style>

+ 0 - 56
src/views/settleManage/financialControl/tabs/rechargeSettle/excelTitle.ts

@@ -66,59 +66,3 @@ export const titleObj = {
     备注: 'remark'
   }
 };
-
-/**
- * 
- *  <el-table-column prop="owner_name" label="商户名称" min-width="200px" v-if="!([4, 5].includes(query.status))">
-          <template #default="scope">
-            <div>{{ scope.row.owner_name }}</div>
-            <div>ID:{{ scope.row.company_uid }}</div>
-          </template>
-        </el-table-column>
-        <el-table-column v-if="!([4, 5].includes(query.status))" prop="business_str" show-overflow-tooltip label="所属商务"
-          min-width="200px">
-        </el-table-column>
-        <el-table-column prop="tixian_money" label="提现金额" show-overflow-tooltip min-width="200px">
-        </el-table-column>
-        <el-table-column v-if="!([4, 5].includes(query.status))" prop="tixian_money" label="打款金额" show-overflow-tooltip
-          min-width="200px">
-        </el-table-column>
-        <el-table-column prop="created_at" show-overflow-tooltip label="提现时间" min-width="200px">
-        </el-table-column>
-        <el-table-column prop="card_no" label="提现账户" show-overflow-tooltip>
-        </el-table-column>
-        <el-table-column prop="status_str" label="状态" show-overflow-tooltip min-width="200px">
-          <template #default="scope">
-            <el-text class="mx-1" :type="colorType(scope.row)">{{ scope.row.status_str }}</el-text>
-            <div v-if="query.status == 4">
-              <el-text class="mx-1" :type="colorType(scope.row)">{{ scope.row.updated_at }}</el-text>
-            </div>
-          </template>
-        </el-table-column>
-        <el-table-column prop="name_of_payee" label="收款人姓名" show-overflow-tooltip min-width="200px">
-        </el-table-column>
-        <el-table-column prop="remark" label="备注" show-overflow-tooltip>
-        </el-table-column>
-        <el-table-column label="操作" v-if="isShowOperate">
-          <template #default="scope">
-            <el-button link type="primary" size="small" v-if="query.status == 1"
-              @click="openType('withdrawDetailVisible', scope.row)">提现详情</el-button>
-            <br />
-            <div v-if="query.status == 1">
-              <el-button link type="primary" size="small" @click="openType('approvedVisible', scope.row)">审核通过</el-button>
-              <br />
-              <el-button link type="primary" size="small"
-                @click="openType('noapprovedVisible', scope.row)">审核不通过</el-button>
-              <br />
-            </div>
-            <div v-if="query.status == 3">
-              <el-button link type="primary" size="small"
-                @click="openType('applyVisible', scope.row, '打款成功')">打款成功</el-button>
-              <br />
-              <el-button link type="primary" size="small"
-                @click="openType('applyVisible', scope.row, '打款失败')">打款失败</el-button>
-              <br />
-            </div>
-          </template>
-        </el-table-column>
- */

+ 1 - 1
vite.config.js

@@ -76,7 +76,7 @@ export default defineConfig(({ command, mode }) => {
             enabledCollections: ['ep'] //@iconify-json/ep 是 Element Plus 的图标库
           }),
           // 自动导入 Element Plus 组件//dev环境会特别慢加上,build的时候可以放开
-          ElementPlusResolver()
+          // ElementPlusResolver()
         ],
         dirs: ['src/components/', 'src/layout/'],