Ver código fonte

添加标签管理功能

boweniac 9 meses atrás
pai
commit
ea756bb5d1

+ 92 - 0
src/api/wechat/label.ts

@@ -0,0 +1,92 @@
+import { defHttp } from '@/utils/http/axios';
+import { ErrorMessageMode } from '/#/axios';
+import { BaseDataResp, BaseListReq, BaseResp, BaseIDsReq, BaseIDReq } from '@/api/model/baseModel';
+import {LabelInfo, LabelListResp, LabelSelectListResp} from './model/labelModel';
+
+enum Api {
+  CreateLabel = '/wechat-api/label/create',
+  UpdateLabel = '/wechat-api/label/update',
+  GetLabelList = '/wechat-api/label/list',
+  GetLabelSelectList = '/wechat-api/label/select_list',
+  DeleteLabel = '/wechat-api/label/delete',
+  GetLabelById = '/wechat-api/label',
+}
+
+/**
+ * @description: Get label select list
+ */
+
+export interface LabelSelectListReq {
+  page: number;
+  pageSize: number;
+  type?: number;
+}
+
+export const getLabelSelectList = (params: LabelSelectListReq, mode: ErrorMessageMode = 'notice') => {
+  return defHttp.post<BaseDataResp<LabelSelectListResp>>(
+    { url: Api.GetLabelSelectList, params },
+    { errorMessageMode: mode },
+  );
+};
+
+/**
+ * @description: Get label list
+ */
+
+export const getLabelList = (params: BaseListReq, mode: ErrorMessageMode = 'notice') => {
+  return defHttp.post<BaseDataResp<LabelListResp>>(
+    { url: Api.GetLabelList, params },
+    { errorMessageMode: mode },
+  );
+};
+
+/**
+ *  @description: Create a new label
+ */
+export const createLabel = (params: LabelInfo, mode: ErrorMessageMode = 'notice') => {
+  return defHttp.post<BaseResp>(
+    { url: Api.CreateLabel, params: params },
+    {
+      errorMessageMode: mode,
+      successMessageMode: mode,
+    },
+  );
+};
+
+/**
+ *  @description: Update the label
+ */
+export const updateLabel = (params: LabelInfo, mode: ErrorMessageMode = 'notice') => {
+  return defHttp.post<BaseResp>(
+    { url: Api.UpdateLabel, params: params },
+    {
+      errorMessageMode: mode,
+      successMessageMode: mode,
+    },
+  );
+};
+
+/**
+ *  @description: Delete labels
+ */
+export const deleteLabel = (params: BaseIDsReq, mode: ErrorMessageMode = 'notice') => {
+  return defHttp.post<BaseResp>(
+    { url: Api.DeleteLabel, params: params },
+    {
+      errorMessageMode: mode,
+      successMessageMode: mode,
+    },
+  );
+};
+
+/**
+ *  @description: Get label By ID
+ */
+export const getLabelById = (params: BaseIDReq, mode: ErrorMessageMode = 'notice') => {
+  return defHttp.post<BaseDataResp<LabelInfo>>(
+    { url: Api.GetLabelById, params: params },
+    {
+      errorMessageMode: mode,
+    },
+  );
+};

+ 92 - 0
src/api/wechat/labelRelationship.ts

@@ -0,0 +1,92 @@
+import { defHttp } from '@/utils/http/axios';
+import { ErrorMessageMode } from '/#/axios';
+import { BaseDataResp, BaseListReq, BaseResp, BaseIDsReq, BaseIDReq } from '@/api/model/baseModel';
+import {
+  LabelRelationshipInfo,
+  LabelRelationshipListResp,
+  LabelRelationshipsInfo
+} from './model/labelRelationshipModel';
+
+enum Api {
+  CreateLabelRelationship = '/wechat-api/label_relationship/create',
+  UpdateLabelRelationship = '/wechat-api/label_relationship/update',
+  UpdateLabelRelationships = '/wechat-api/label_relationship/update_contact_labels',
+  GetLabelRelationshipList = '/wechat-api/label_relationship/list',
+  DeleteLabelRelationship = '/wechat-api/label_relationship/delete',
+  GetLabelRelationshipById = '/wechat-api/label_relationship',
+}
+
+/**
+ * @description: Get label relationship list
+ */
+
+export const getLabelRelationshipList = (params: BaseListReq, mode: ErrorMessageMode = 'notice') => {
+  return defHttp.post<BaseDataResp<LabelRelationshipListResp>>(
+    { url: Api.GetLabelRelationshipList, params },
+    { errorMessageMode: mode },
+  );
+};
+
+/**
+ *  @description: Create a new label relationship
+ */
+export const createLabelRelationship = (params: LabelRelationshipInfo, mode: ErrorMessageMode = 'notice') => {
+  return defHttp.post<BaseResp>(
+    { url: Api.CreateLabelRelationship, params: params },
+    {
+      errorMessageMode: mode,
+      successMessageMode: mode,
+    },
+  );
+};
+
+/**
+ *  @description: Update the label relationship
+ */
+export const updateLabelRelationship = (params: LabelRelationshipInfo, mode: ErrorMessageMode = 'notice') => {
+  return defHttp.post<BaseResp>(
+    { url: Api.UpdateLabelRelationship, params: params },
+    {
+      errorMessageMode: mode,
+      successMessageMode: mode,
+    },
+  );
+};
+
+/**
+ *  @description: Update the label relationships
+ */
+export const updateLabelRelationships = (params: LabelRelationshipsInfo, mode: ErrorMessageMode = 'notice') => {
+  return defHttp.post<BaseResp>(
+    { url: Api.UpdateLabelRelationships, params: params },
+    {
+      errorMessageMode: mode,
+      successMessageMode: mode,
+    },
+  );
+};
+
+/**
+ *  @description: Delete label relationships
+ */
+export const deleteLabelRelationship = (params: BaseIDsReq, mode: ErrorMessageMode = 'notice') => {
+  return defHttp.post<BaseResp>(
+    { url: Api.DeleteLabelRelationship, params: params },
+    {
+      errorMessageMode: mode,
+      successMessageMode: mode,
+    },
+  );
+};
+
+/**
+ *  @description: Get label relationship By ID
+ */
+export const getLabelRelationshipById = (params: BaseIDReq, mode: ErrorMessageMode = 'notice') => {
+  return defHttp.post<BaseDataResp<LabelRelationshipInfo>>(
+    { url: Api.GetLabelRelationshipById, params: params },
+    {
+      errorMessageMode: mode,
+    },
+  );
+};

+ 34 - 0
src/api/wechat/model/labelModel.ts

@@ -0,0 +1,34 @@
+import { BaseListResp } from '@/api/model/baseModel';
+import {LabelRelationshipInfo} from "@/api/wechat/model/labelRelationshipModel";
+
+/**
+ *  @description: Label info response
+ */
+export interface LabelInfo {
+  id?: number;
+  createdAt?: number;
+  updatedAt?: number;
+  status?: number;
+  type?: number;
+  name?: string;
+  from?: number;
+  mode?: number;
+  conditions?: string;
+  labelRelationships?: LabelRelationshipInfo[];
+}
+
+/**
+ *  @description: Label info response
+ */
+export interface LabelSelectListInfo {
+  Label?: string;
+  Value?: number;
+}
+
+/**
+ *  @description: Label list response
+ */
+
+export type LabelListResp = BaseListResp<LabelInfo>;
+
+export type LabelSelectListResp = BaseListResp<LabelSelectListInfo>;

+ 32 - 0
src/api/wechat/model/labelRelationshipModel.ts

@@ -0,0 +1,32 @@
+import { BaseListResp } from '@/api/model/baseModel';
+import {LabelInfo} from "@/api/wechat/model/labelModel";
+import {ContactInfo} from "@/api/wechat/model/contactModel";
+
+/**
+ *  @description: LabelRelationship info response
+ */
+export interface LabelRelationshipInfo {
+  id?: number;
+  createdAt?: number;
+  updatedAt?: number;
+  status?: number;
+  labelId?: number;
+  contactId?: number;
+  contact?: ContactInfo;
+  label?: LabelInfo;
+}
+
+export interface LabelRelationshipsInfo {
+  id?: number;
+  createdAt?: number;
+  updatedAt?: number;
+  status?: number;
+  labelIds: number[];
+  contactId: number;
+}
+
+/**
+ *  @description: LabelRelationship list response
+ */
+
+export type LabelRelationshipListResp = BaseListResp<LabelRelationshipInfo>;

+ 63 - 0
src/locales/lang/en/wechat.ts

@@ -44,4 +44,67 @@ export default {
     editContact: 'Edit Contact',
     contactList: 'Contact List',
   },
+  label: {
+    status: 'Status',
+    name: 'Name',
+    from: 'From',
+    mode: 'Mode',
+    dynamicParam: 'DynamicParam',
+    staticParam: 'StaticParam',
+    addLabel: 'Add Label',
+    editLabel: 'Edit Label',
+    labelList: 'Label List',
+  },
+  label_from: ['Created by the background', 'Synchronize from WeChat'],
+  label_mode: ['Dynamic label', 'Static label'],
+  labelRelationship: {
+    status: 'Status',
+    labelId: 'LabelId',
+    contactId: 'ContactId',
+    contact: 'Contact',
+    label: 'Label',
+    addLabelRelationship: 'Add LabelRelationship',
+    editLabelRelationship: 'Edit LabelRelationship',
+    labelRelationshipList: 'LabelRelationship List',
+  },
+  labelRelationship: {
+    status: 'Status',
+    labelId: 'LabelId',
+    contactId: 'ContactId',
+    contact: 'Contact',
+    label: 'Label',
+    addLabelRelationship: 'Add LabelRelationship',
+    editLabelRelationship: 'Edit LabelRelationship',
+    labelRelationshipList: 'LabelRelationship List',
+  },
+  labelRelationship: {
+    status: 'Status',
+    labelId: 'LabelId',
+    contactId: 'ContactId',
+    contact: 'Contact',
+    label: 'Label',
+    addLabelRelationship: 'Add LabelRelationship',
+    editLabelRelationship: 'Edit LabelRelationship',
+    labelRelationshipList: 'LabelRelationship List',
+  },
+  labelRelationship: {
+    status: 'Status',
+    labelId: 'LabelId',
+    contactId: 'ContactId',
+    contact: 'Contact',
+    label: 'Label',
+    addLabelRelationship: 'Add LabelRelationship',
+    editLabelRelationship: 'Edit LabelRelationship',
+    labelRelationshipList: 'LabelRelationship List',
+  },
+  labelRelationship: {
+    status: 'Status',
+    labelId: 'LabelId',
+    contactId: 'ContactId',
+    contact: 'Contact',
+    label: 'Label',
+    addLabelRelationship: 'Add LabelRelationship',
+    editLabelRelationship: 'Edit LabelRelationship',
+    labelRelationshipList: 'LabelRelationship List',
+  },
 };

+ 25 - 0
src/locales/lang/zh-CN/wechat.ts

@@ -41,8 +41,33 @@ export default {
     gid: '群ID',
     gname: '群名称',
     v3: 'V3',
+    labels: '标签',
     addContact: '添加 Contact',
     editContact: '编辑 Contact',
     contactList: 'Contact 列表',
   },
+  label: {
+    status: 'Status',
+    type: '标签类型',
+    name: '标签名称',
+    from: '标签来源',
+    mode: '标签模式',
+    conditions: '触发规则',
+    addLabel: '添加标签',
+    editLabel: '编辑标签',
+    labelList: '标签列表',
+  },
+  label_type: ['好友', '群组', '公众号', '企业微信联系人'],
+  label_from: ['后台创建', '个微同步'],
+  label_mode: ['动态标签', '静态标签'],
+  labelRelationship: {
+    status: 'Status',
+    labelId: 'LabelId',
+    contactId: 'ContactId',
+    contact: 'Contact',
+    label: 'Label',
+    addLabelRelationship: '添加 LabelRelationship',
+    editLabelRelationship: '编辑 LabelRelationship',
+    labelRelationshipList: 'LabelRelationship 列表',
+  },
 };

+ 10 - 2
src/views/wechat/contact/ContactDrawer.vue

@@ -17,7 +17,8 @@
   import { BasicDrawer, useDrawerInner } from '@/components/Drawer';
   import { useI18n } from 'vue-i18n';
 
-  import { createContact, updateContact } from '@/api/wechat/contact';
+  import { createContact } from '@/api/wechat/contact';
+  import {updateLabelRelationships} from "@/api/wechat/labelRelationship";
 
   export default defineComponent({
     name: 'ContactDrawer',
@@ -57,7 +58,14 @@
         console.log(values,'values')
         setDrawerProps({ confirmLoading: true });
         values['id'] = unref(isUpdate) ? Number(values['id']) : 0;
-        let result = unref(isUpdate) ? await updateContact(values) : await createContact(values);
+
+        const labelRelationshipsInfo = {
+          status: 1,
+          labelIds: values.labelRelationships,
+          contactId: values.id,
+        };
+
+        let result = unref(isUpdate) ? await updateLabelRelationships(labelRelationshipsInfo) : await createContact(values);
         if (result.code === 0) {
           closeDrawer();
           emit('success');

+ 136 - 102
src/views/wechat/contact/contact.data.ts

@@ -1,9 +1,6 @@
 import { BasicColumn, FormSchema } from '@/components/Table';
 import { useI18n } from '@/hooks/web/useI18n';
-import { formatToDateTime } from '@/utils/dateUtil';
-import { updateContact } from '@/api/wechat/contact';
-import { Switch } from 'ant-design-vue';
-import { h } from 'vue';
+import {getLabelSelectList} from "@/api/wechat/label";
 const { t } = useI18n();
 
 export const columns: BasicColumn[] = [
@@ -29,13 +26,32 @@ export const columns: BasicColumn[] = [
   },
   {
     title: t('wechat.contact.lag'),
-    dataIndex: 'lag',
-    width: 80,
+    dataIndex: 'labelRelationships',
+    width: 200,
+    customRender: ({ text }) => {
+      return text.map(label => label.label).join(', ');
+    },
   },
 ];
 
 export const searchFormSchema: FormSchema[] = [
   {
+    field: 'labelIDs',
+    label: t('wechat.contact.labels'),
+    component: 'ApiSelect',
+    componentProps: {
+      api: () => getLabelSelectList({ page: 1, pageSize: 100 }).then(response => {
+        return response.data.map((label) => {
+          return { label: label.label, value: label.value };
+        });
+      }),
+      mode: 'tags',
+      tokenSeparators: [','],
+      change: 'handleChange', // 这里你需要在你的组件中定义 handleChange 函数
+    },
+    colProps: { span: 8 },
+  },
+  {
     field: 'wxWxid',
     label: t('wechat.contact.wxWxid'),
     component: 'Input',
@@ -63,105 +79,123 @@ export const formSchema: FormSchema[] = [
     show: false,
   },
   {
-    field: 'wxWxid',
-    label: t('wechat.contact.wxWxid'),
-    component: 'Input',
-    required: true,
-  },
-  {
     field: 'type',
     label: t('wechat.contact.type'),
     component: 'InputNumber',
+    show: false,
+    required: false,
+  },
+  {
+    field: 'labelRelationships',
+    label: t('wechat.contact.labels'),
+    component: 'ApiSelect',
+    componentProps: (renderCallbackParams) => ({
+      api: (params) => getLabelSelectList(params).then(response => {
+        return response.data.map((label) => {
+          return { label: label.label, value: label.value };
+        });
+      }),
+      params: { page: 1, pageSize: 100, type: renderCallbackParams.formModel.type },
+      mode: 'tags',
+      tokenSeparators: [','],
+      change: 'handleChange', // 这里你需要在你的组件中定义 handleChange 函数
+    }),
     required: true,
   },
-  {
-    field: 'wxid',
-    label: t('wechat.contact.wxid'),
-    component: 'Input',
-    required: true,
-  },
-  {
-    field: 'account',
-    label: t('wechat.contact.account'),
-    component: 'Input',
-    required: true,
-  },
-  {
-    field: 'nickname',
-    label: t('wechat.contact.nickname'),
-    component: 'Input',
-    required: true,
-  },
-  {
-    field: 'markname',
-    label: t('wechat.contact.markname'),
-    component: 'Input',
-    required: true,
-  },
-  {
-    field: 'headimg',
-    label: t('wechat.contact.headimg'),
-    component: 'Input',
-    required: true,
-  },
-  {
-    field: 'sex',
-    label: t('wechat.contact.sex'),
-    component: 'InputNumber',
-    required: true,
-  },
-  {
-    field: 'starrole',
-    label: t('wechat.contact.starrole'),
-    component: 'Input',
-    required: true,
-  },
-  {
-    field: 'dontseeit',
-    label: t('wechat.contact.dontseeit'),
-    component: 'InputNumber',
-    required: true,
-  },
-  {
-    field: 'dontseeme',
-    label: t('wechat.contact.dontseeme'),
-    component: 'InputNumber',
-    required: true,
-  },
-  {
-    field: 'lag',
-    label: t('wechat.contact.lag'),
-    component: 'Input',
-    required: true,
-  },
-  {
-    field: 'gid',
-    label: t('wechat.contact.gid'),
-    component: 'Input',
-    required: true,
-  },
-  {
-    field: 'gname',
-    label: t('wechat.contact.gname'),
-    component: 'Input',
-    required: true,
-  },
-  {
-    field: 'v3',
-    label: t('wechat.contact.v3'),
-    component: 'Input',
-    required: true,
-  },
-  {
-    field: 'status',
-    label: t('wechat.contact.status'),
-    component: 'RadioButtonGroup',
-    defaultValue: 1,
-    componentProps: {
-      options: [
-        { label: t('common.on'), value: 1 },
-        { label: t('common.off'), value: 2 },
-      ],
-    },
-  },
+  // {
+  //   field: 'wxWxid',
+  //   label: t('wechat.contact.wxWxid'),
+  //   component: 'Input',
+  //   required: true,
+  // },
+  // {
+  //   field: 'wxid',
+  //   label: t('wechat.contact.wxid'),
+  //   component: 'Input',
+  //   required: true,
+  // },
+  // {
+  //   field: 'account',
+  //   label: t('wechat.contact.account'),
+  //   component: 'Input',
+  //   required: true,
+  // },
+  // {
+  //   field: 'nickname',
+  //   label: t('wechat.contact.nickname'),
+  //   component: 'Input',
+  //   required: true,
+  // },
+  // {
+  //   field: 'markname',
+  //   label: t('wechat.contact.markname'),
+  //   component: 'Input',
+  //   required: true,
+  // },
+  // {
+  //   field: 'headimg',
+  //   label: t('wechat.contact.headimg'),
+  //   component: 'Input',
+  //   required: true,
+  // },
+  // {
+  //   field: 'sex',
+  //   label: t('wechat.contact.sex'),
+  //   component: 'InputNumber',
+  //   required: true,
+  // },
+  // {
+  //   field: 'starrole',
+  //   label: t('wechat.contact.starrole'),
+  //   component: 'Input',
+  //   required: true,
+  // },
+  // {
+  //   field: 'dontseeit',
+  //   label: t('wechat.contact.dontseeit'),
+  //   component: 'InputNumber',
+  //   required: true,
+  // },
+  // {
+  //   field: 'dontseeme',
+  //   label: t('wechat.contact.dontseeme'),
+  //   component: 'InputNumber',
+  //   required: true,
+  // },
+  // {
+  //   field: 'lag',
+  //   label: t('wechat.contact.lag'),
+  //   component: 'Input',
+  //   required: true,
+  // },
+  // {
+  //   field: 'gid',
+  //   label: t('wechat.contact.gid'),
+  //   component: 'Input',
+  //   required: true,
+  // },
+  // {
+  //   field: 'gname',
+  //   label: t('wechat.contact.gname'),
+  //   component: 'Input',
+  //   required: true,
+  // },
+  // {
+  //   field: 'v3',
+  //   label: t('wechat.contact.v3'),
+  //   component: 'Input',
+  //   required: true,
+  // },
+  // {
+  //   field: 'status',
+  //   label: t('wechat.contact.status'),
+  //   component: 'RadioButtonGroup',
+  //   defaultValue: 1,
+  //   componentProps: {
+  //     options: [
+  //       { label: t('common.on'), value: 1 },
+  //       { label: t('common.off'), value: 2 },
+  //     ],
+  //   },
+  // },
 ];

+ 25 - 5
src/views/wechat/contact/index.vue

@@ -13,9 +13,9 @@
         </Button>
       </template>
       <template #toolbar>
-        <a-button type="primary" @click="handleCreate">
-          {{ t('wechat.contact.addContact') }}
-        </a-button>
+<!--        <a-button type="primary" @click="handleCreate">-->
+<!--          {{ t('wechat.contact.addContact') }}-->
+<!--        </a-button>-->
       </template>
       <template #bodyCell="{ column, record }" >
         <template v-if="column.key === 'action'">
@@ -48,7 +48,7 @@
   </div>
 </template>
 <script lang="ts">
-  import { createVNode, defineComponent, ref } from 'vue';
+import {createVNode, defineComponent, onMounted, ref, watch} from 'vue';
   import { Modal } from 'ant-design-vue';
   import { ExclamationCircleOutlined } from '@ant-design/icons-vue/lib/icons';
   import { BasicTable, useTable, TableAction } from '@/components/Table';
@@ -59,8 +59,9 @@
   import SendMsgDrawer from './SendMsgDrawer.vue'
   import { useI18n } from 'vue-i18n';
 
-  import { columns, searchFormSchema } from './contact.data';
+import {columns, formSchema, searchFormSchema} from './contact.data';
   import { getContactList, deleteContact } from '@/api/wechat/contact';
+  import {getLabelList} from "@/api/wechat/label";
 
   export default defineComponent({
     name: 'ContactManagement',
@@ -151,6 +152,23 @@
         await reload();
       }
 
+      // const labelOptions = ref([]);
+      //
+      // async function fetchLabelList() {
+      //   const response = await getLabelList({ page: 1, pageSize: 100 });
+      //   const labels = response.data;
+      //
+      //   labelOptions.value = labels.map((label) => {
+      //     return { label: label.name, value: label.id };
+      //   });
+      // }
+      //
+      // onMounted(fetchLabelList);
+      //
+      // function getLabelOptions() {
+      //     return labelOptions.value;
+      // }
+
       return {
         t,
         registerTable,
@@ -164,6 +182,8 @@
         handleSuccess1,
         handleBatchDelete,
         showDeleteButton,
+        // labelOptions,
+        // getLabelOptions,
       };
     },
   });

+ 75 - 0
src/views/wechat/label/LabelDrawer.vue

@@ -0,0 +1,75 @@
+<template>
+  <BasicDrawer
+    v-bind="$attrs"
+    @register="registerDrawer"
+    showFooter
+    :title="getTitle"
+    width="500px"
+    @ok="handleSubmit"
+  >
+    <BasicForm @register="registerForm" />
+  </BasicDrawer>
+</template>
+<script lang="ts">
+  import { defineComponent, ref, computed, unref } from 'vue';
+  import { BasicForm, useForm } from '@/components/Form/index';
+  import { formSchema } from './label.data';
+  import { BasicDrawer, useDrawerInner } from '@/components/Drawer';
+  import { useI18n } from 'vue-i18n';
+
+  import { createLabel, updateLabel } from '@/api/wechat/label';
+
+  export default defineComponent({
+    name: 'LabelDrawer',
+    components: { BasicDrawer, BasicForm },
+    emits: ['success', 'register'],
+    setup(_, { emit }) {
+      const isUpdate = ref(true);
+      const { t } = useI18n();
+
+      const [registerForm, { resetFields, setFieldsValue, validate }] = useForm({
+        labelWidth: 160,
+        baseColProps: { span: 24 },
+        layout: 'vertical',
+        schemas: formSchema,
+        showActionButtonGroup: false,
+      });
+
+      const [registerDrawer, { setDrawerProps, closeDrawer }] = useDrawerInner(async (data) => {
+        resetFields();
+        setDrawerProps({ confirmLoading: false });
+
+        isUpdate.value = !!data?.isUpdate;
+
+        if (unref(isUpdate)) {
+          setFieldsValue({
+            ...data.record,
+          });
+        }
+      });
+
+      const getTitle = computed(() =>
+        !unref(isUpdate) ? t('wechat.label.addLabel') : t('wechat.label.editLabel'),
+      );
+
+      async function handleSubmit() {
+        const values = await validate();
+        setDrawerProps({ confirmLoading: true });
+        values['id'] = unref(isUpdate) ? Number(values['id']) : 0;
+        let result = unref(isUpdate) ? await updateLabel(values) : await createLabel(values);
+        if (result.code === 0) {
+          closeDrawer();
+          emit('success');
+        }
+        setDrawerProps({ confirmLoading: false });
+      }
+
+      return {
+        registerDrawer,
+        registerForm,
+        getTitle,
+        handleSubmit,
+      };
+    },
+  });
+</script>

+ 151 - 0
src/views/wechat/label/index.vue

@@ -0,0 +1,151 @@
+<template>
+  <div>
+    <BasicTable @register="registerTable">
+      <template #tableTitle>
+        <Button
+          type="primary"
+          danger
+          preIcon="ant-design:delete-outlined"
+          v-if="showDeleteButton"
+          @click="handleBatchDelete"
+        >
+          {{ t('common.delete') }}
+        </Button>
+      </template>
+      <template #toolbar>
+        <a-button type="primary" @click="handleCreate">
+          {{ t('wechat.label.addLabel') }}
+        </a-button>
+      </template>
+      <template #bodyCell="{ column, record }">
+        <template v-if="column.key === 'action'">
+          <TableAction
+            :actions="[
+              {
+                icon: 'clarity:note-edit-line',
+                onClick: handleEdit.bind(null, record),
+              },
+              {
+                icon: 'ant-design:delete-outlined',
+                color: 'error',
+                popConfirm: {
+                  title: t('common.deleteConfirm'),
+                  placement: 'left',
+                  confirm: handleDelete.bind(null, record),
+                },
+              },
+            ]"
+          />
+        </template>
+      </template>
+    </BasicTable>
+    <LabelDrawer @register="registerDrawer" @success="handleSuccess" />
+  </div>
+</template>
+<script lang="ts">
+  import { createVNode, defineComponent, ref } from 'vue';
+  import { Modal } from 'ant-design-vue';
+  import { ExclamationCircleOutlined } from '@ant-design/icons-vue/lib/icons';
+  import { BasicTable, useTable, TableAction } from '@/components/Table';
+  import { Button } from '@/components/Button';
+
+  import { useDrawer } from '@/components/Drawer';
+  import LabelDrawer from './LabelDrawer.vue';
+  import { useI18n } from 'vue-i18n';
+
+  import { columns, searchFormSchema } from './label.data';
+  import { getLabelList, deleteLabel } from '@/api/wechat/label';
+
+  export default defineComponent({
+    name: 'LabelManagement',
+    components: { BasicTable, LabelDrawer, TableAction, Button },
+    setup() {
+      const { t } = useI18n();
+      const selectedIds = ref<number[] | string[]>();
+      const showDeleteButton = ref<boolean>(false);
+
+      const [registerDrawer, { openDrawer }] = useDrawer();
+      const [registerTable, { reload }] = useTable({
+        title: t('wechat.label.labelList'),
+        api: getLabelList,
+        columns,
+        formConfig: {
+          labelWidth: 120,
+          schemas: searchFormSchema,
+        },
+        useSearchForm: true,
+        showTableSetting: true,
+        bordered: true,
+        showIndexColumn: false,
+        clickToRowSelect: false,
+        actionColumn: {
+          width: 30,
+          title: t('common.action'),
+          dataIndex: 'action',
+          fixed: undefined,
+        },
+        rowKey: 'id',
+        rowSelection: {
+          type: 'checkbox',
+          onChange: (selectedRowKeys, _selectedRows) => {
+            selectedIds.value = selectedRowKeys as number[];
+            showDeleteButton.value = selectedRowKeys.length > 0;
+          },
+        },
+      });
+
+      function handleCreate() {
+        openDrawer(true, {
+          isUpdate: false,
+        });
+      }
+
+      function handleEdit(record: Recordable) {
+        openDrawer(true, {
+          record,
+          isUpdate: true,
+        });
+      }
+
+      async function handleDelete(record: Recordable) {
+        const result = await deleteLabel({ ids: [record.id] });
+        if (result.code === 0) {
+          await reload();
+        }
+      }
+
+      async function handleBatchDelete() {
+        Modal.confirm({
+          title: t('common.deleteConfirm'),
+          icon: createVNode(ExclamationCircleOutlined),
+          async onOk() {
+            const result = await deleteLabel({ ids: selectedIds.value as number[] });
+            if (result.code === 0) {
+              showDeleteButton.value = false;
+              await reload();
+            }
+          },
+          onCancel() {
+            console.log('Cancel');
+          },
+        });
+      }
+
+      async function handleSuccess() {
+        await reload();
+      }
+
+      return {
+        t,
+        registerTable,
+        registerDrawer,
+        handleCreate,
+        handleEdit,
+        handleDelete,
+        handleSuccess,
+        handleBatchDelete,
+        showDeleteButton,
+      };
+    },
+  });
+</script>

+ 186 - 0
src/views/wechat/label/label.data.ts

@@ -0,0 +1,186 @@
+import { BasicColumn, FormSchema } from '@/components/Table';
+import { useI18n } from '@/hooks/web/useI18n';
+import { formatToDateTime } from '@/utils/dateUtil';
+import { updateLabel } from '@/api/wechat/label';
+import { Switch } from 'ant-design-vue';
+import { h } from 'vue';
+
+const { t } = useI18n();
+
+export const columns: BasicColumn[] = [
+  {
+    title: t('wechat.label.type'),
+    dataIndex: 'type',
+    width: 100,
+    customRender: ({ record }) => {
+      return t(`wechat.label_type[${record.type-1}]`);
+    },
+  },
+  {
+    title: t('wechat.label.name'),
+    dataIndex: 'name',
+    width: 100,
+  },
+  {
+    title: t('wechat.label.from'),
+    dataIndex: 'from',
+    width: 100,
+    customRender: ({ record }) => {
+      return t(`wechat.label_from[${record.from-1}]`);
+    },
+  },
+  {
+    title: t('wechat.label.mode'),
+    dataIndex: 'mode',
+    width: 100,
+    customRender: ({ record }) => {
+      return t(`wechat.label_mode[${record.mode-1}]`);
+    },
+  },
+  {
+    title: t('common.status'),
+    dataIndex: 'status',
+    width: 50,
+    customRender: ({ record }) => {
+      if (!Reflect.has(record, 'pendingStatus')) {
+        record.pendingStatus = false;
+      }
+      return h(Switch, {
+        checked: record.status === 1,
+        checkedChildren: t('common.on'),
+        unCheckedChildren: t('common.off'),
+        loading: record.pendingStatus,
+        onChange(checked, _) {
+          record.pendingStatus = true;
+          const newStatus = checked ? 1 : 2;
+          updateLabel({ id: record.id, status: newStatus })
+            .then(() => {
+              record.status = newStatus;
+            })
+            .finally(() => {
+              record.pendingStatus = false;
+            });
+        },
+      });
+    },
+  },
+  {
+    title: t('common.createTime'),
+    dataIndex: 'createdAt',
+    width: 70,
+    customRender: ({ record }) => {
+      return formatToDateTime(record.createdAt);
+    },
+  },
+];
+
+export const searchFormSchema: FormSchema[] = [
+  {
+    field: 'type',
+    label: t('wechat.label.type'),
+    component: 'Select',
+    colProps: { span: 8 },
+    componentProps: {
+      options: [
+        { label: t(`wechat.label_type[0]`), value: 1 },
+        { label: t(`wechat.label_type[1]`), value: 2 },
+        { label: t(`wechat.label_type[2]`), value: 3 },
+        { label: t(`wechat.label_type[3]`), value: 4 },
+      ],
+    },
+  },
+  {
+    field: 'name',
+    label: t('wechat.label.name'),
+    component: 'Input',
+    colProps: { span: 8 },
+  },
+  {
+    field: 'from',
+    label: t('wechat.label.from'),
+    component: 'Select',
+    colProps: { span: 8 },
+    componentProps: {
+      options: [
+        { label: t(`wechat.label_from[0]`), value: 1 },
+        { label: t(`wechat.label_from[1]`), value: 2 },
+      ],
+    },
+  },
+  {
+    field: 'mode',
+    label: t('wechat.label.mode'),
+    component: 'Select',
+    colProps: { span: 8 },
+    componentProps: {
+      options: [
+        { label: t(`wechat.label_mode[0]`), value: 1 },
+        { label: t(`wechat.label_mode[1]`), value: 2 },
+      ],
+    },
+  },
+];
+
+export const formSchema: FormSchema[] = [
+  {
+    field: 'id',
+    label: 'ID',
+    component: 'Input',
+    show: false,
+  },
+  {
+    field: 'type',
+    label: t('wechat.label.type'),
+    component: 'RadioGroup',
+    required: true,
+    componentProps: {
+      options: [
+        { label: t(`wechat.label_type[0]`), value: 1 },
+        { label: t(`wechat.label_type[1]`), value: 2 },
+        { label: t(`wechat.label_type[2]`), value: 3 },
+        { label: t(`wechat.label_type[3]`), value: 4 },
+      ],
+    },
+    dynamicDisabled: (renderCallbackParams) => {
+      return renderCallbackParams.model.id !== undefined; // 你需要在你的组件中定义 isEditMode
+    },
+  },
+  {
+    field: 'name',
+    label: t('wechat.label.name'),
+    component: 'Input',
+    required: true,
+  },
+  {
+    field: 'from',
+    label: t('wechat.label.from'),
+    component: 'RadioGroup',
+    required: true,
+    componentProps: {
+      options: [
+        { label: t(`wechat.label_from[0]`), value: 1 },
+        { label: t(`wechat.label_from[1]`), value: 2 },
+      ],
+    },
+    show: false,
+    defaultValue: 1,
+  },
+  {
+    field: 'mode',
+    label: t('wechat.label.mode'),
+    component: 'RadioGroup',
+    required: true,
+    componentProps: {
+      options: [
+        { label: t(`wechat.label_mode[0]`), value: 1 },
+        { label: t(`wechat.label_mode[1]`), value: 2 },
+      ],
+    },
+  },
+  {
+    field: 'conditions',
+    label: t('wechat.label.conditions'),
+    component: 'Input',
+    required: true,
+  },
+];

+ 75 - 0
src/views/wechat/label_relationship/LabelRelationshipDrawer.vue

@@ -0,0 +1,75 @@
+<template>
+  <BasicDrawer
+    v-bind="$attrs"
+    @register="registerDrawer"
+    showFooter
+    :title="getTitle"
+    width="500px"
+    @ok="handleSubmit"
+  >
+    <BasicForm @register="registerForm" />
+  </BasicDrawer>
+</template>
+<script lang="ts">
+  import { defineComponent, ref, computed, unref } from 'vue';
+  import { BasicForm, useForm } from '@/components/Form/index';
+  import { formSchema } from './labelRelationship.data';
+  import { BasicDrawer, useDrawerInner } from '@/components/Drawer';
+  import { useI18n } from 'vue-i18n';
+
+  import { createLabelRelationship, updateLabelRelationship } from '@/api/wechat/labelRelationship';
+
+  export default defineComponent({
+    name: 'LabelRelationshipDrawer',
+    components: { BasicDrawer, BasicForm },
+    emits: ['success', 'register'],
+    setup(_, { emit }) {
+      const isUpdate = ref(true);
+      const { t } = useI18n();
+
+      const [registerForm, { resetFields, setFieldsValue, validate }] = useForm({
+        labelWidth: 160,
+        baseColProps: { span: 24 },
+        layout: 'vertical',
+        schemas: formSchema,
+        showActionButtonGroup: false,
+      });
+
+      const [registerDrawer, { setDrawerProps, closeDrawer }] = useDrawerInner(async (data) => {
+        resetFields();
+        setDrawerProps({ confirmLoading: false });
+
+        isUpdate.value = !!data?.isUpdate;
+
+        if (unref(isUpdate)) {
+          setFieldsValue({
+            ...data.record,
+          });
+        }
+      });
+
+      const getTitle = computed(() =>
+        !unref(isUpdate) ? t('wechat.labelRelationship.addLabelRelationship') : t('wechat.labelRelationship.editLabelRelationship'),
+      );
+
+      async function handleSubmit() {
+        const values = await validate();
+        setDrawerProps({ confirmLoading: true });
+        values['id'] = unref(isUpdate) ? Number(values['id']) : 0;
+        let result = unref(isUpdate) ? await updateLabelRelationship(values) : await createLabelRelationship(values);
+        if (result.code === 0) {
+          closeDrawer();
+          emit('success');
+        }
+        setDrawerProps({ confirmLoading: false });
+      }
+
+      return {
+        registerDrawer,
+        registerForm,
+        getTitle,
+        handleSubmit,
+      };
+    },
+  });
+</script>

+ 151 - 0
src/views/wechat/label_relationship/index.vue

@@ -0,0 +1,151 @@
+<template>
+  <div>
+    <BasicTable @register="registerTable">
+      <template #tableTitle>
+        <Button
+          type="primary"
+          danger
+          preIcon="ant-design:delete-outlined"
+          v-if="showDeleteButton"
+          @click="handleBatchDelete"
+        >
+          {{ t('common.delete') }}
+        </Button>
+      </template>
+      <template #toolbar>
+        <a-button type="primary" @click="handleCreate">
+          {{ t('wechat.labelRelationship.addLabelRelationship') }}
+        </a-button>
+      </template>
+      <template #bodyCell="{ column, record }">
+        <template v-if="column.key === 'action'">
+          <TableAction
+            :actions="[
+              {
+                icon: 'clarity:note-edit-line',
+                onClick: handleEdit.bind(null, record),
+              },
+              {
+                icon: 'ant-design:delete-outlined',
+                color: 'error',
+                popConfirm: {
+                  title: t('common.deleteConfirm'),
+                  placement: 'left',
+                  confirm: handleDelete.bind(null, record),
+                },
+              },
+            ]"
+          />
+        </template>
+      </template>
+    </BasicTable>
+    <LabelRelationshipDrawer @register="registerDrawer" @success="handleSuccess" />
+  </div>
+</template>
+<script lang="ts">
+  import { createVNode, defineComponent, ref } from 'vue';
+  import { Modal } from 'ant-design-vue';
+  import { ExclamationCircleOutlined } from '@ant-design/icons-vue/lib/icons';
+  import { BasicTable, useTable, TableAction } from '@/components/Table';
+  import { Button } from '@/components/Button';
+
+  import { useDrawer } from '@/components/Drawer';
+  import LabelRelationshipDrawer from './LabelRelationshipDrawer.vue';
+  import { useI18n } from 'vue-i18n';
+
+  import { columns, searchFormSchema } from './labelRelationship.data';
+  import { getLabelRelationshipList, deleteLabelRelationship } from '@/api/wechat/labelRelationship';
+
+  export default defineComponent({
+    name: 'LabelRelationshipManagement',
+    components: { BasicTable, LabelRelationshipDrawer, TableAction, Button },
+    setup() {
+      const { t } = useI18n();
+      const selectedIds = ref<number[] | string[]>();
+      const showDeleteButton = ref<boolean>(false);
+
+      const [registerDrawer, { openDrawer }] = useDrawer();
+      const [registerTable, { reload }] = useTable({
+        title: t('wechat.labelRelationship.labelRelationshipList'),
+        api: getLabelRelationshipList,
+        columns,
+        formConfig: {
+          labelWidth: 120,
+          schemas: searchFormSchema,
+        },
+        useSearchForm: true,
+        showTableSetting: true,
+        bordered: true,
+        showIndexColumn: false,
+        clickToRowSelect: false,
+        actionColumn: {
+          width: 30,
+          title: t('common.action'),
+          dataIndex: 'action',
+          fixed: undefined,
+        },
+        rowKey: 'id',
+        rowSelection: {
+          type: 'checkbox',
+          onChange: (selectedRowKeys, _selectedRows) => {
+            selectedIds.value = selectedRowKeys as number[];
+            showDeleteButton.value = selectedRowKeys.length > 0;
+          },
+        },
+      });
+
+      function handleCreate() {
+        openDrawer(true, {
+          isUpdate: false,
+        });
+      }
+
+      function handleEdit(record: Recordable) {
+        openDrawer(true, {
+          record,
+          isUpdate: true,
+        });
+      }
+
+      async function handleDelete(record: Recordable) {
+        const result = await deleteLabelRelationship({ ids: [record.id] });
+        if (result.code === 0) {
+          await reload();
+        }
+      }
+
+      async function handleBatchDelete() {
+        Modal.confirm({
+          title: t('common.deleteConfirm'),
+          icon: createVNode(ExclamationCircleOutlined),
+          async onOk() {
+            const result = await deleteLabelRelationship({ ids: selectedIds.value as number[] });
+            if (result.code === 0) {
+              showDeleteButton.value = false;
+              await reload();
+            }
+          },
+          onCancel() {
+            console.log('Cancel');
+          },
+        });
+      }
+
+      async function handleSuccess() {
+        await reload();
+      }
+
+      return {
+        t,
+        registerTable,
+        registerDrawer,
+        handleCreate,
+        handleEdit,
+        handleDelete,
+        handleSuccess,
+        handleBatchDelete,
+        showDeleteButton,
+      };
+    },
+  });
+</script>

+ 114 - 0
src/views/wechat/label_relationship/labelRelationship.data.ts

@@ -0,0 +1,114 @@
+import { BasicColumn, FormSchema } from '@/components/Table';
+import { useI18n } from '@/hooks/web/useI18n';
+import { formatToDateTime } from '@/utils/dateUtil';
+import { updateLabelRelationship } from '@/api/wechat/labelRelationship';
+import { Switch } from 'ant-design-vue';
+import { h } from 'vue';
+
+const { t } = useI18n();
+
+export const columns: BasicColumn[] = [
+  {
+    title: t('wechat.labelRelationship.labelId'),
+    dataIndex: 'labelId',
+    width: 100,
+  },
+  {
+    title: t('wechat.labelRelationship.contactId'),
+    dataIndex: 'contactId',
+    width: 100,
+  },
+  {
+    title: t('wechat.labelRelationship.contact'),
+    dataIndex: 'contact',
+    width: 100,
+  },
+  {
+    title: t('wechat.labelRelationship.label'),
+    dataIndex: 'label',
+    width: 100,
+  },
+  {
+    title: t('common.status'),
+    dataIndex: 'status',
+    width: 50,
+    customRender: ({ record }) => {
+      if (!Reflect.has(record, 'pendingStatus')) {
+        record.pendingStatus = false;
+      }
+      return h(Switch, {
+        checked: record.status === 1,
+        checkedChildren: t('common.on'),
+        unCheckedChildren: t('common.off'),
+        loading: record.pendingStatus,
+        onChange(checked, _) {
+          record.pendingStatus = true;
+          const newStatus = checked ? 1 : 2;
+          updateLabelRelationship({ id: record.id, status: newStatus })
+            .then(() => {
+              record.status = newStatus;
+            })
+            .finally(() => {
+              record.pendingStatus = false;
+            });
+        },
+      });
+    },
+  },
+  {
+    title: t('common.createTime'),
+    dataIndex: 'createdAt',
+    width: 70,
+    customRender: ({ record }) => {
+      return formatToDateTime(record.createdAt);
+    },
+  },
+];
+
+export const searchFormSchema: FormSchema[] = [
+];
+
+export const formSchema: FormSchema[] = [
+  {
+    field: 'id',
+    label: 'ID',
+    component: 'Input',
+    show: false,
+  },
+  {
+    field: 'labelId',
+    label: t('wechat.labelRelationship.labelId'),
+    component: 'InputNumber',
+    required: true,
+  },
+  {
+    field: 'contactId',
+    label: t('wechat.labelRelationship.contactId'),
+    component: 'InputNumber',
+    required: true,
+  },
+  {
+    field: 'contact',
+    label: t('wechat.labelRelationship.contact'),
+    component: 'Input',
+    required: true,
+  },
+  {
+    field: 'label',
+    label: t('wechat.labelRelationship.label'),
+    component: 'Input',
+    required: true,
+  },
+  {
+    field: 'status',
+    label: t('wechat.labelRelationship.status'),
+    component: 'RadioButtonGroup',
+    defaultValue: 1,
+    componentProps: {
+      options: [
+        { label: t('common.on'), value: 1 },
+        { label: t('common.off'), value: 2 },
+      ],
+    },
+  },
+];

+ 8 - 8
vite.config.ts

@@ -21,14 +21,14 @@ export default defineApplicationConfig({
           ws: true,
           rewrite: (path) => path.replace(new RegExp(`^/api`), ''),
         },
-        // '/sys-api': {
-        //   target: 'http://localhost:9100',
-        //   changeOrigin: true,
-        //   ws: true,
-        //   rewrite: (path) => path.replace(new RegExp(`^/sys-api`), ''),
-        //   // only https
-        //   // secure: false
-        // },
+        '/sys-api': {
+          target: 'http://localhost:9100',
+          changeOrigin: true,
+          ws: true,
+          rewrite: (path) => path.replace(new RegExp(`^/sys-api`), ''),
+          // only https
+          // secure: false
+        },
         '/fms-api': {
           target: 'http://localhost:9102',
           changeOrigin: true,