From 5b3e02b447fb3b7f19ed26abd040d2e84b52ab6a Mon Sep 17 00:00:00 2001
From: qsh <>
Date: Fri, 7 Jun 2024 17:01:46 +0800
Subject: [PATCH] =?UTF-8?q?=E6=8F=90=E4=BA=A4?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/api/clue/clueField.js                     |  40 ++++
 src/api/clue/clueGetSet.js                    |  21 ++
 src/api/clue/orderField.js                    |  40 ++++
 src/api/clue/source.js                        |  31 +++
 src/config/axios/service.ts                   |   2 +-
 src/views/Clue/Set/Comp/ClueGet.vue           |  86 +++----
 src/views/Clue/Set/Comp/ClueSend.vue          |  65 ++++--
 src/views/Clue/Set/Comp/ClueSource.vue        |  77 ++++---
 src/views/Clue/Set/Comp/DialogSource.vue      |  37 +--
 src/views/Clue/Set/Comp/FieldClue.vue         | 216 +++++++++++++-----
 src/views/Clue/Set/Comp/FieldOrder.vue        | 216 +++++++++++++-----
 src/views/Clue/Set/index.vue                  |  45 ++--
 src/views/Clue/Skill/index.vue                |  28 ++-
 .../Inventory/Comp/InventoryRecord.vue        |   8 +-
 .../MiniMall/Inventory/Comp/Warehouse.vue     |   9 +-
 15 files changed, 682 insertions(+), 239 deletions(-)
 create mode 100644 src/api/clue/clueField.js
 create mode 100644 src/api/clue/clueGetSet.js
 create mode 100644 src/api/clue/orderField.js
 create mode 100644 src/api/clue/source.js

diff --git a/src/api/clue/clueField.js b/src/api/clue/clueField.js
new file mode 100644
index 0000000..2d386a2
--- /dev/null
+++ b/src/api/clue/clueField.js
@@ -0,0 +1,40 @@
+import request from '@/config/axios'
+
+// 创建
+export const createField = (data) => {
+  return request.post({ url: '/admin-api/crm/clue-param/create', data })
+}
+
+// 更新
+export const updateField = (data) => {
+  return request.put({ url: '/admin-api/crm/clue-param/update', data })
+}
+
+// 删除
+export const deleteField = (id) => {
+  return request.delete({ url: `/admin-api/crm/clue-param/delete?id=${id}` })
+}
+
+// 获得
+export const getField = (id) => {
+  return request.get({ url: `/admin-api/crm/clue-param/get?id=${id}` })
+}
+
+// 获得精简信息列表
+export const getSimpleFieldList = () => {
+  return request.get({ url: '/admin-api/crm/clue-param/simple-list' })
+}
+
+// 获取自定义字段
+export const getDiyFieldList = () => {
+  return request.get({ url: '/admin-api/crm/clue-param/get-diy-param' })
+}
+
+// 状态修改
+export const updateFieldStatus = (signParamId, status) => {
+  const data = {
+    signParamId,
+    status
+  }
+  return request.put({ url: '/admin-api/crm/clue-param/status/update', data: data })
+}
diff --git a/src/api/clue/clueGetSet.js b/src/api/clue/clueGetSet.js
new file mode 100644
index 0000000..989dbe2
--- /dev/null
+++ b/src/api/clue/clueGetSet.js
@@ -0,0 +1,21 @@
+import request from '@/config/axios'
+
+// 线索获取规则
+export const getClueGainRuleList = () => {
+  return request.get({ url: '/admin-api/crm/sch-clue-gain-rule/list' })
+}
+
+// 删除
+export const deleteClueGainRule = (id) => {
+  return request.delete({ url: `/admin-api/crm/sch-clue-gain-rule/delete?id=${id}` })
+}
+
+// 线索规则
+export const getClueDistributeRuleList = () => {
+  return request.get({ url: '/admin-api/crm/sch-clue-gain-rule/list' })
+}
+
+// 删除
+export const deleteClueDistributeRule = (id) => {
+  return request.delete({ url: `/admin-api/crm/sch-clue-gain-rule/delete?id=${id}` })
+}
diff --git a/src/api/clue/orderField.js b/src/api/clue/orderField.js
new file mode 100644
index 0000000..23dd285
--- /dev/null
+++ b/src/api/clue/orderField.js
@@ -0,0 +1,40 @@
+import request from '@/config/axios'
+
+// 创建
+export const createField = (data) => {
+  return request.post({ url: '/admin-api/crm/sign-param/create', data })
+}
+
+// 更新
+export const updateField = (data) => {
+  return request.put({ url: '/admin-api/crm/sign-param/update', data })
+}
+
+// 删除
+export const deleteField = (id) => {
+  return request.delete({ url: `/admin-api/crm/sign-param/delete?id=${id}` })
+}
+
+// 获得
+export const getField = (id) => {
+  return request.get({ url: `/admin-api/crm/sign-param/get?id=${id}` })
+}
+
+// 获得精简信息列表
+export const getSimpleFieldList = () => {
+  return request.get({ url: '/admin-api/crm/sign-param/simple-list' })
+}
+
+// 获取自定义字段
+export const getDiyFieldList = () => {
+  return request.get({ url: '/admin-api/crm/sign-param/get-diy-param' })
+}
+
+// 状态修改
+export const updateFieldStatus = (signParamId, status) => {
+  const data = {
+    signParamId,
+    status
+  }
+  return request.put({ url: '/admin-api/crm/sign-param/status/update', data: data })
+}
diff --git a/src/api/clue/source.js b/src/api/clue/source.js
new file mode 100644
index 0000000..d0d3890
--- /dev/null
+++ b/src/api/clue/source.js
@@ -0,0 +1,31 @@
+import request from '@/config/axios'
+
+// 查询(精简)列表
+export const getSimpleSourceList = async () => {
+  return await request.get({ url: '/admin-api/crm/source/list-all-simple' })
+}
+
+// 查询列表
+export const getSourcePage = async (params) => {
+  return await request.get({ url: '/admin-api/crm/source/list', params })
+}
+
+// 查询详情
+export const getSource = async (id) => {
+  return await request.get({ url: '/admin-api/crm/source/get?id=' + id })
+}
+
+// 新增
+export const createSource = async (data) => {
+  return await request.post({ url: '/admin-api/crm/source/create', data: data })
+}
+
+// 修改
+export const updateSource = async (params) => {
+  return await request.put({ url: '/admin-api/crm/source/update', data: params })
+}
+
+// 删除
+export const deleteSource = async (id) => {
+  return await request.delete({ url: '/admin-api/crm/source/delete?id=' + id })
+}
diff --git a/src/config/axios/service.ts b/src/config/axios/service.ts
index 9453910..bca3e1f 100644
--- a/src/config/axios/service.ts
+++ b/src/config/axios/service.ts
@@ -179,7 +179,7 @@ service.interceptors.response.use(
         })
       }
     } else if (code === 500) {
-      ElMessage.error(t('sys.api.errMsg500'))
+      ElMessage.error(t(msg || 'sys.api.errMsg500'))
       return Promise.reject(new Error(msg))
     } else if (code === 901) {
       ElMessage.error({
diff --git a/src/views/Clue/Set/Comp/ClueGet.vue b/src/views/Clue/Set/Comp/ClueGet.vue
index 038a7e2..9e33da0 100644
--- a/src/views/Clue/Set/Comp/ClueGet.vue
+++ b/src/views/Clue/Set/Comp/ClueGet.vue
@@ -1,10 +1,10 @@
 <template>
   <div>
-    <el-table :data="list" border>
+    <el-table v-loading="loading" :data="list" border>
       <el-table-column type="index" width="50" />
       <el-table-column label="来源名称" width="200px">
         <template #default="{ row }">
-          <el-input v-model="row.name" placeholder="请输入" :clearable="false" />
+          <el-input v-model="row.source" placeholder="请输入" :clearable="false" />
         </template>
       </el-table-column>
       <el-table-column label="渠道" width="300px">
@@ -24,14 +24,14 @@
               :value="item.value"
             />
           </el-select>
-          <span>{{ row.resourceName }}</span>
+          <span>{{ row.channel }}</span>
         </template>
       </el-table-column>
-      <el-table-column label="获取连接" prop="link" />
+      <el-table-column label="获取连接" prop="url" />
       <el-table-column label="参数详情" prop="params" />
       <el-table-column label="是否启用" width="100px">
         <template #default="{ row }">
-          <el-switch v-model="row.inEnable" :active-value="true" :inactive-value="false" />
+          <el-switch v-model="row.status" :active-value="0" :inactive-value="1" />
         </template>
       </el-table-column>
       <el-table-column label="操作" width="100px">
@@ -49,56 +49,60 @@
   </div>
 </template>
 
-<script setup>
-const list = ref([
-  {
-    name: '一点通账号1',
-    resourceName: '驾校一点通',
-    link: 'https://sscrm.ahduima.com/clue/get?cid=1001&aid=1001&res=2',
-    params: '参数详情'
-  },
-  {
-    name: '一点通账号2',
-    resourceName: '驾校一点通',
-    link: 'https://sscrm.ahduima.com/clue/get?cid=1001&aid=1001&res=2',
-    params: '参数详情'
-  },
-  {
-    name: '宝典账号',
-    resourceName: '驾考宝典',
-    link: 'https://sscrm.ahduima.com/clue/get?cid=1001&aid=1001&res=1',
-    params: '参数详情'
-  },
-  {
-    name: '抖音',
-    resourceName: '抖音/开心学车',
-    link: 'https://sscrm.ahduima.com/clue/get?cid=1001&aid=1001&res=3',
-    params: '参数详情'
-  }
-])
+<script setup name="ClueSet">
+import { getClueGainRuleList, deleteClueGainRule } from '@/api/clue/clueGetSet'
+
+const message = useMessage() // 消息弹窗
+const { t } = useI18n() // 国际化
 
-const resourceOptions = ref([
-  { value: 1, label: '驾考宝典' },
-  { value: 2, label: '一点通' },
-  { value: 3, label: '抖音' }
-])
+const list = ref([])
+const loading = ref(false)
+const resourceOptions = ref([])
+
+async function getList() {
+  loading.value = true
+  try {
+    const data = await getClueGainRuleList()
+    list.value = data
+  } finally {
+    loading.value = false
+  }
+}
 
 function handleInsert() {
-  list.value.push({ name: '', link: '', edit: true, inEnable: true })
+  list.value.push({ source: '', url: '', edit: true, status: 0 })
 }
 
 function onSubmit() {
   console.log('保存成功')
 }
 
-function handleRemove(idx) {
-  list.value.splice(idx, 1)
+async function handleRemove(idx) {
+  if (list.value[idx].ruleId) {
+    try {
+      // 删除的二次确认
+      await message.delConfirm()
+      // 发起删除
+      await deleteClueGainRule(list.value[idx].ruleId)
+      message.success(t('common.delSuccess'))
+      // 刷新列表
+      await getList()
+    } catch (err) {
+      console.log(err)
+    }
+  } else {
+    list.value.splice(idx, 1)
+  }
 }
 
 function handleChange(row) {
-  row.link = `https://sscrm.ahduima.com/clue/get?cid=1001&aid=1001&res=${row.resource}`
+  row.url = `https://sscrm.ahduima.com/clue/get?cid=1001&aid=1001&res=${row.resource}`
   row.params = '参数详情'
 }
+
+onMounted(() => {
+  getList()
+})
 </script>
 
 <style lang="scss" scoped></style>
diff --git a/src/views/Clue/Set/Comp/ClueSend.vue b/src/views/Clue/Set/Comp/ClueSend.vue
index 161f7ce..358973c 100644
--- a/src/views/Clue/Set/Comp/ClueSend.vue
+++ b/src/views/Clue/Set/Comp/ClueSend.vue
@@ -2,22 +2,33 @@
   <div class="flex">
     <div class="mr-20px" style="width: 500px">
       <el-input
-        v-model="searchForm.keyword"
+        v-model="searchForm.nickname"
         placeholder="请输入关键字查询"
         clearable
         class="mb-10px"
         @keyup.enter="getUserList"
       />
 
-      <el-table :data="userList" @cell-click="selectUser">
-        <el-table-column prop="name" label="员工姓名" />
-        <el-table-column prop="phone" label="电话" />
-        <el-table-column prop="workNum" label="工号" />
+      <el-table
+        v-loading="loading"
+        :data="userList"
+        :row-class-name="setRowClass"
+        @row-click="handleRowClick"
+      >
+        <el-table-column prop="nickname" label="员工姓名" />
+        <el-table-column prop="mobile" label="电话" />
+        <el-table-column prop="status" label="状态">
+          <template #default="{ row }">
+            {{ ['在职', '离职'][row.status] }}
+          </template>
+        </el-table-column>
       </el-table>
       <!-- 分页 -->
       <Pagination
         v-model:limit="searchForm.pageSize"
-        v-model:page="searchForm.pageNum"
+        v-model:page="searchForm.pageNo"
+        small
+        layout="total, prev, pager, next, jumper"
         :total="total"
         @pagination="getUserList"
       />
@@ -65,16 +76,27 @@
   </div>
 </template>
 
-<script setup>
+<script setup name="ClueSend">
+import { getUserPage } from '@/api/system/user'
+
+// const message = useMessage() // 消息弹窗
+// const { t } = useI18n() // 国际化
+
 const searchForm = ref({
-  keyword: '',
+  nickname: '',
   pageSize: 20,
-  pageNum: 1
+  pageNo: 1
 })
 
 const total = ref(0)
+const loading = ref(false)
+const userList = ref([])
+
+function setRowClass({ row }) {
+  return row.field == currentRowId.value ? 'current-row' : ''
+}
 
-const userList = ref([{ name: '张三', phone: '1888888888', workNum: '202101030001' }])
+const currentRowId = ref('')
 
 const form = ref({
   isAuto: 1,
@@ -84,8 +106,18 @@ const form = ref({
 })
 const rules = ref({})
 
-function getUserList() {
-  console.log('获取列表')
+async function getUserList() {
+  loading.value = true
+  try {
+    const data = await getUserPage(searchForm.value)
+    userList.value = data.list
+    if (userList.value.length) {
+      handleRowClick(userList.value[0])
+    }
+    total.value = data.total
+  } finally {
+    loading.value = false
+  }
 }
 
 function onSubmit() {
@@ -111,9 +143,14 @@ function resourceCheckedChange(val) {
   resourceIndeterminate.value = checkedCount > 0 && checkedCount < resourceOptions.value.length
 }
 
-function selectUser(row) {
-  console.log(row)
+function handleRowClick(row) {
+  currentRowId.value = row.ruleId
+  form.value = { ...row }
 }
+
+onMounted(() => {
+  getUserList()
+})
 </script>
 
 <style lang="scss" scoped></style>
diff --git a/src/views/Clue/Set/Comp/ClueSource.vue b/src/views/Clue/Set/Comp/ClueSource.vue
index 3968c0f..e27a7a2 100644
--- a/src/views/Clue/Set/Comp/ClueSource.vue
+++ b/src/views/Clue/Set/Comp/ClueSource.vue
@@ -1,9 +1,9 @@
 <template>
   <div>
-    <el-form ref="queryForm" :model="searchForm" label-width="0" inline>
+    <el-form ref="queryForm" :model="searchForm" label-width="0" inline @submit.prevent>
       <el-form-item>
         <el-input
-          v-model="searchForm.name"
+          v-model="searchForm.sourceName"
           placeholder="请输入名称"
           clearable
           @keyup.enter="handleQuery"
@@ -22,84 +22,89 @@
       :tree-props="{ children: 'children' }"
     >
       <el-table-column prop="sourceName" label="来源名称" />
-      <el-table-column prop="orderNum" label="排序" width="100px" />
+      <el-table-column prop="sort" label="排序" width="100px" />
       <el-table-column prop="remark" label="备注" />
-      <el-table-column label="创建时间" prop="createTime" width="180px" />
-      <el-table-column label="创建人" prop="createUser" width="150px" />
+      <el-table-column
+        label="创建时间"
+        prop="createTime"
+        width="180px"
+        :formatter="dateFormatter"
+      />
+      <el-table-column label="状态">
+        <template #default="{ row }">
+          <dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="row.status" />
+        </template>
+      </el-table-column>
       <el-table-column label="操作">
-        <template #default="scope">
-          <el-button type="primary" text @click="openForm('update', scope.row)">修改</el-button>
-          <el-button type="primary" text @click="openForm('createChildren', scope.row)"
-            >新增</el-button
-          >
-          <el-button type="danger" text @click="handleDelete(scope.row)">删除</el-button>
+        <template #default="{ row }">
+          <el-button type="primary" text @click="openForm('update', row)">修改</el-button>
+          <el-button type="primary" text @click="openForm('createChildren', row)"> 新增 </el-button>
+          <el-button type="danger" text @click="handleDelete(row.sourceId)">删除</el-button>
         </template>
       </el-table-column>
     </el-table>
-    <Pagination
-      v-model:limit="searchForm.pageSize"
-      v-model:page="searchForm.pageNum"
-      :total="total"
-      @pagination="handleQuery"
-    />
 
     <DialogSource ref="sourceDialog" @success="handleQuery" />
   </div>
 </template>
 
 <script setup name="ClueSource">
+import { handleTree } from '@/utils/tree'
+import * as SourceApi from '@/api/clue/source'
+import { dateFormatter } from '@/utils/formatTime'
 import DialogSource from './DialogSource.vue'
+import { DICT_TYPE } from '@/utils/dict'
+
+const message = useMessage() // 消息弹窗
+const { t } = useI18n() // 国际化
 
 const searchForm = ref({
-  pageNum: 1,
-  pageSize: 20
+  sourceName: undefined
 })
 
-const total = ref(0)
 const sourceDialog = ref()
 const tableList = ref([])
 const loading = ref(false)
 
 function handleQuery() {
-  searchForm.value.pageNum = 1
   getList()
 }
 function resetQuery() {
   searchForm.value = {
-    question: '',
-    pageSize: 20,
-    pageNum: 1
+    sourceName: ''
   }
   getList()
 }
 
-function getList() {
-  tableList.value = [
-    {
-      sourceId: 1,
-      sourceName: '测试',
-      level: 1,
-      children: [{ sourceId: 1001, sourceName: '二级来源', level: 2, parentSource: '测试' }]
-    }
-  ]
+async function getList() {
+  loading.value = true
+  try {
+    const data = await SourceApi.getSourcePage(searchForm.value)
+    tableList.value = handleTree(data, 'sourceId')
+  } finally {
+    loading.value = false
+  }
 }
 
 function openForm(type, info) {
   sourceDialog.value.open(type, info)
 }
 
-async function handleDelete(row) {
+async function handleDelete(id) {
   try {
-    console.log(row)
     // 删除的二次确认
     await message.delConfirm()
     // 发起删除
-    // await UserApi.deleteUser(row.id)
+    await SourceApi.deleteSource(id)
     message.success(t('common.delSuccess'))
     // 刷新列表
     await getList()
   } catch {}
 }
+
+onMounted(() => {
+  getList()
+})
 </script>
 
 <style lang="scss" scoped></style>
diff --git a/src/views/Clue/Set/Comp/DialogSource.vue b/src/views/Clue/Set/Comp/DialogSource.vue
index 524f85e..9c3c337 100644
--- a/src/views/Clue/Set/Comp/DialogSource.vue
+++ b/src/views/Clue/Set/Comp/DialogSource.vue
@@ -7,14 +7,20 @@
       :rules="formRules"
       label-width="80px"
     >
-      <el-form-item v-if="formData.level > 1" label="上级来源">
+      <el-form-item v-if="formData.parentSource" label="上级来源">
         <el-input v-model="formData.parentSource" disabled />
       </el-form-item>
       <el-form-item label="来源名称" prop="sourceName">
         <el-input v-model="formData.sourceName" placeholder="请输入来源名称" />
       </el-form-item>
-      <el-form-item label="排序" prop="orderNum">
-        <el-input v-model="formData.orderNum" placeholder="请输入排序" type="number" :min="0" />
+      <el-form-item label="排序" prop="sort">
+        <el-input v-model="formData.sort" placeholder="请输入排序" type="number" :min="0" />
+      </el-form-item>
+      <el-form-item label="状态" prop="status">
+        <el-radio-group v-model="formData.status">
+          <el-radio :label="0"> 启用 </el-radio>
+          <el-radio :label="1"> 禁用 </el-radio>
+        </el-radio-group>
       </el-form-item>
       <el-form-item label="备注" prop="remark">
         <el-input
@@ -32,6 +38,8 @@
   </Dialog>
 </template>
 <script name="DialogSource" setup>
+import * as SourceApi from '@/api/clue/source'
+
 const { t } = useI18n() // 国际化
 const message = useMessage() // 消息弹窗
 
@@ -41,7 +49,7 @@ const formLoading = ref(false) // 表单的加载中:1)修改时的数据加
 const formType = ref('') // 表单的类型:create - 新增;update - 修改
 const formData = ref({
   sourceName: '',
-  orderNum: 1,
+  sort: 1,
   remark: ''
 })
 const formRules = reactive({
@@ -56,16 +64,15 @@ const open = async (type, info) => {
   formType.value = type
   resetForm()
   // 修改时,设置数据
-  if (info.sourceId) {
+  if (info?.sourceId) {
     formLoading.value = true
     try {
       if (type == 'update') {
-        formData.value = { ...info }
+        formData.value = await SourceApi.getSource(info.sourceId)
       } else {
-        formData.value.level = info.level + 1
         formData.value.parentSource = info.sourceName
+        formData.value.parentId = info.sourceId
       }
-      // formData.value = await UserApi.getUser(id)
     } finally {
       formLoading.value = false
     }
@@ -83,13 +90,12 @@ const submitForm = async () => {
   // 提交请求
   formLoading.value = true
   try {
-    // const data = formData.value as unknown as UserApi.UserVO
-    if (formType.value === 'create') {
-      // await UserApi.createUser(data)
-      message.success(t('common.createSuccess'))
-    } else {
-      // await UserApi.updateUser(data)
+    if (formType.value === 'update') {
+      await SourceApi.updateSource(formData.value)
       message.success(t('common.updateSuccess'))
+    } else {
+      await SourceApi.createSource(formData.value)
+      message.success(t('common.createSuccess'))
     }
     dialogVisible.value = false
     // 发送操作成功的事件
@@ -103,7 +109,8 @@ const submitForm = async () => {
 const resetForm = () => {
   formData.value = {
     sourceName: '',
-    orderNum: 1,
+    status: 0,
+    sort: 1,
     remark: ''
   }
   formRef.value?.resetFields()
diff --git a/src/views/Clue/Set/Comp/FieldClue.vue b/src/views/Clue/Set/Comp/FieldClue.vue
index 4e4ebfd..625fb17 100644
--- a/src/views/Clue/Set/Comp/FieldClue.vue
+++ b/src/views/Clue/Set/Comp/FieldClue.vue
@@ -2,45 +2,55 @@
   <el-row :gutter="80">
     <el-col :span="10" :offset="0">
       <el-button class="mb-10px" type="primary" @click="handleInsert">新增属性</el-button>
-      <el-table :data="tableList">
-        <el-table-column prop="name" label="名称" />
-        <el-table-column label="启用状态">
+      <el-table :data="tableList" :row-class-name="setRowClass" @row-click="handleRowClick">
+        <el-table-column prop="label" label="名称" />
+        <el-table-column prop="field" label="属性编码" />
+        <el-table-column prop="component" label="类型" width="200px">
+          <template #default="{ row }">
+            {{ typeOptions.find((it) => it.value == row.component).label }}
+          </template>
+        </el-table-column>
+        <el-table-column label="启用状态" width="100">
           <template #default="{ row }">
             <el-switch
               v-model="row.status"
-              :active-value="1"
-              :inactive-value="0"
-              :disabled="!row.canUpdate"
+              :active-value="0"
+              :inactive-value="1"
               @change="changeStatus(row)"
             />
           </template>
         </el-table-column>
         <el-table-column label="操作" width="80px">
           <template #default="{ row }">
-            <el-button
-              type="primary"
-              text
-              :disabled="!row.canUpdate"
-              style="padding: 0"
-              @click="remove(row)"
+            <el-button type="primary" text style="padding: 0" @click="remove(row.clueParamId)"
               >删除</el-button
             >
           </template>
         </el-table-column>
       </el-table>
     </el-col>
-    <el-col :span="14" :offset="0">
-      <el-form :model="form" ref="fieldForm" :rules="rules" label-width="80px" :inline="false">
-        <el-form-item label="属性名称" prop="name">
-          <el-input v-model="form.name" placeholder="请输入属性名称" />
+    <el-col :span="14" :offset="0" v-if="tableList.length || formType == 'create'">
+      <el-form :model="form" ref="fieldForm" :rules="rules" label-width="100px" :inline="false">
+        <el-form-item label="属性名称" prop="label">
+          <el-input v-model="form.label" placeholder="请输入属性名称" />
+        </el-form-item>
+        <el-form-item prop="field">
+          <template #label>
+            <div class="flex justify-center" style="align-items: center">
+              <span>属性编码</span>
+              <Tooltip message="请输入字母或数字,必须以字母开头" />
+            </div>
+          </template>
+          <el-input v-model="form.field" placeholder="请输入属性编码" />
         </el-form-item>
-        <el-form-item label="属性类型" prop="type">
+        <el-form-item label="属性类型" prop="component">
           <el-select
-            v-model="form.type"
+            v-model="form.component"
             placeholder="请选择属性类型"
             clearable
             filterable
             style="width: 100%"
+            @change="form.options = []"
           >
             <el-option
               v-for="item in typeOptions"
@@ -51,72 +61,174 @@
           </el-select>
         </el-form-item>
         <el-form-item
-          v-if="['Checkbox', 'Radio', 'Select'].includes(form.type)"
+          v-if="['Checkbox', 'Radio', 'Select'].includes(form.component)"
           label="选项"
-          prop="option"
-          key="option"
+          prop="options"
+          key="options"
         >
           <div>
-            <el-button type="primary" @click="optionList.push([])"> 新增选项 </el-button>
+            <el-button type="primary" @click="handleAddOption"> 新增选项 </el-button>
             <div
               class="flex justify-between mt-10px"
-              v-for="(item, index) in optionList"
+              v-for="(item, index) in form.options"
               :key="index"
             >
-              <el-input v-model="item.label" placeholder="请输入选项内容" clearable />
-              <el-button type="primary" text @click="optionList.splice(index, 1)">删除</el-button>
+              <el-input
+                v-model="item.name"
+                placeholder="请输入选项内容"
+                clearable
+                style="width: 300px !important"
+                @blur="item.id = item.name"
+              />
+              <el-button type="primary" text @click="form.options.splice(index, 1)">删除</el-button>
             </div>
           </div>
         </el-form-item>
         <el-form-item>
-          <el-button type="primary" @click="onSubmit">保存</el-button>
+          <el-button type="primary" :disabled="formLoading" @click="onSubmit">保存</el-button>
         </el-form-item>
       </el-form>
     </el-col>
   </el-row>
 </template>
 
-<script setup>
-const tableList = ref([{ name: '咨询车型', status: 0, canUpdate: false }])
+<script setup name="FieldClue">
+import * as FieldApi from '@/api/clue/clueField'
+import { getDictOptions } from '@/utils/dict'
+
+const message = useMessage() // 消息弹窗
+const { t } = useI18n() // 国际化
+
+const tableList = ref([])
+const currentRowId = ref('')
+
+function setRowClass({ row }) {
+  return row.field == currentRowId.value ? 'current-row' : ''
+}
+
+function handleRowClick(row) {
+  formType.value = 'update'
+  currentRowId.value = row.field
+  form.value = { ...row }
+}
 
 const form = ref({
-  name: undefined,
-  type: undefined,
-  option: undefined
+  label: undefined,
+  field: '',
+  component: undefined,
+  options: [],
+  status: 0,
+  isCustom: true,
+  isForm: true,
+  isSearch: true,
+  isTable: true
 })
 
-const typeOptions = ref([
-  { label: '输入框', value: 'Input' },
-  { label: '多选', value: 'Checkbox' },
-  { label: '单选', value: 'Radio' },
-  { label: '下拉选', value: 'Select' },
-  { label: '开关', value: 'Switch' },
-  { label: '日期选择', value: 'DatePicker' },
-  { label: '时间选择', value: 'TimePicker' },
-  { label: '富文本', value: 'Editor' },
-  { label: '图片', value: 'UploadImg' },
-  { label: '文件', value: 'UploadFile' }
-])
+const loading = ref(false)
+
+const typeOptions = ref([])
 
-const rules = {}
+const rules = {
+  label: { required: true, message: '名称不可为空', trigger: 'blur' },
+  field: { required: true, message: '编码不可为空', trigger: 'blur' },
+  component: { required: true, message: '类型不可为空', trigger: 'change' }
+}
+
+async function getList() {
+  loading.value = true
+  try {
+    const data = await FieldApi.getDiyFieldList()
+    tableList.value = data
+    if (data.length) {
+      handleRowClick(data[0])
+    }
+  } finally {
+    loading.value = false
+  }
+}
 
-const optionList = ref([])
+const formType = ref('')
 
 function handleInsert() {
-  console.log('新增')
+  formType.value = 'create'
+  form.value = {
+    label: undefined,
+    field: '',
+    component: undefined,
+    options: [],
+    status: 0,
+    isCustom: true,
+    isForm: true,
+    isSearch: true,
+    isTable: true
+  }
 }
 
-function changeStatus(row) {
-  console.log(row.status)
+async function remove(id) {
+  try {
+    // 删除的二次确认
+    await message.delConfirm()
+    // 发起删除
+    await FieldApi.deleteField(id)
+    message.success(t('common.delSuccess'))
+    // 刷新列表
+    await getList()
+  } catch (err) {
+    console.log(err)
+  }
 }
 
-function remove(row) {
-  console.log(row.status)
+async function changeStatus(row) {
+  try {
+    // 二次确认
+    await message.confirm('是否确认修改状态')
+    await FieldApi.updateFieldStatus(row.clueParamId, row.status)
+    message.success('修改成功')
+    // 刷新列表
+    await getList()
+  } catch {
+    // 取消后,进行恢复按钮
+    row.status = row.status == 0 ? 1 : 0
+  }
 }
 
-function onSubmit() {
-  console.log('保存')
+const fieldForm = ref()
+const formLoading = ref(false)
+
+function handleAddOption() {
+  if (form.value.options) {
+    form.value.options.push({})
+  } else {
+    form.value.options = []
+  }
+}
+
+async function onSubmit() {
+  // 校验表单
+  if (!fieldForm.value) return
+  const valid = await fieldForm.value.validate()
+  if (!valid) return
+  // 提交请求
+  formLoading.value = true
+  try {
+    if (formType.value === 'create') {
+      await FieldApi.createField(form.value)
+      message.success(t('common.createSuccess'))
+    } else {
+      await FieldApi.updateField(form.value)
+      message.success(t('common.updateSuccess'))
+    }
+    // 发送操作成功的事件
+    getList()
+  } finally {
+    formLoading.value = false
+  }
 }
+
+onMounted(() => {
+  typeOptions.value = getDictOptions('attribute_type')
+  getList()
+})
 </script>
 
 <style lang="scss" scoped></style>
diff --git a/src/views/Clue/Set/Comp/FieldOrder.vue b/src/views/Clue/Set/Comp/FieldOrder.vue
index dae2103..2a8dbf9 100644
--- a/src/views/Clue/Set/Comp/FieldOrder.vue
+++ b/src/views/Clue/Set/Comp/FieldOrder.vue
@@ -2,45 +2,55 @@
   <el-row :gutter="80">
     <el-col :span="10" :offset="0">
       <el-button class="mb-10px" type="primary" @click="handleInsert">新增属性</el-button>
-      <el-table :data="tableList">
-        <el-table-column prop="name" label="名称" />
-        <el-table-column label="启用状态">
+      <el-table :data="tableList" :row-class-name="setRowClass" @row-click="handleRowClick">
+        <el-table-column prop="label" label="名称" />
+        <el-table-column prop="field" label="属性编码" />
+        <el-table-column prop="component" label="类型" width="200px">
+          <template #default="{ row }">
+            {{ typeOptions.find((it) => it.value == row.component).label }}
+          </template>
+        </el-table-column>
+        <el-table-column label="启用状态" width="100">
           <template #default="{ row }">
             <el-switch
               v-model="row.status"
-              :active-value="1"
-              :inactive-value="0"
-              :disabled="!row.canUpdate"
+              :active-value="0"
+              :inactive-value="1"
               @change="changeStatus(row)"
             />
           </template>
         </el-table-column>
         <el-table-column label="操作" width="80px">
           <template #default="{ row }">
-            <el-button
-              type="primary"
-              text
-              :disabled="!row.canUpdate"
-              style="padding: 0"
-              @click="remove(row)"
+            <el-button type="primary" text style="padding: 0" @click="remove(row.clueParamId)"
               >删除</el-button
             >
           </template>
         </el-table-column>
       </el-table>
     </el-col>
-    <el-col :span="14" :offset="0">
-      <el-form :model="form" ref="fieldForm" :rules="rules" label-width="80px" :inline="false">
-        <el-form-item label="属性名称" prop="name">
-          <el-input v-model="form.name" placeholder="请输入属性名称" />
+    <el-col :span="14" :offset="0" v-if="tableList.length || formType == 'create'">
+      <el-form :model="form" ref="fieldForm" :rules="rules" label-width="100px" :inline="false">
+        <el-form-item label="属性名称" prop="label">
+          <el-input v-model="form.label" placeholder="请输入属性名称" />
+        </el-form-item>
+        <el-form-item prop="field">
+          <template #label>
+            <div class="flex justify-center" style="align-items: center">
+              <span>属性编码</span>
+              <Tooltip message="请输入字母或数字,必须以字母开头" />
+            </div>
+          </template>
+          <el-input v-model="form.field" placeholder="请输入属性编码" />
         </el-form-item>
-        <el-form-item label="属性类型" prop="type">
+        <el-form-item label="属性类型" prop="component">
           <el-select
-            v-model="form.type"
+            v-model="form.component"
             placeholder="请选择属性类型"
             clearable
             filterable
             style="width: 100%"
+            @change="form.options = []"
           >
             <el-option
               v-for="item in typeOptions"
@@ -51,72 +61,174 @@
           </el-select>
         </el-form-item>
         <el-form-item
-          v-if="['Checkbox', 'Radio', 'Select'].includes(form.type)"
+          v-if="['Checkbox', 'Radio', 'Select'].includes(form.component)"
           label="选项"
-          prop="option"
-          key="option"
+          prop="options"
+          key="options"
         >
           <div>
-            <el-button type="primary" @click="optionList.push([])"> 新增选项 </el-button>
+            <el-button type="primary" @click="handleAddOption"> 新增选项 </el-button>
             <div
               class="flex justify-between mt-10px"
-              v-for="(item, index) in optionList"
+              v-for="(item, index) in form.options"
               :key="index"
             >
-              <el-input v-model="item.label" placeholder="请输入选项内容" clearable />
-              <el-button type="primary" text @click="optionList.splice(index, 1)">删除</el-button>
+              <el-input
+                v-model="item.name"
+                placeholder="请输入选项内容"
+                clearable
+                style="width: 300px !important"
+                @blur="item.id = item.name"
+              />
+              <el-button type="primary" text @click="form.options.splice(index, 1)">删除</el-button>
             </div>
           </div>
         </el-form-item>
         <el-form-item>
-          <el-button type="primary" @click="onSubmit">保存</el-button>
+          <el-button type="primary" :disabled="formLoading" @click="onSubmit">保存</el-button>
         </el-form-item>
       </el-form>
     </el-col>
   </el-row>
 </template>
 
-<script setup>
-const tableList = ref([{ name: '额外支出费用', status: 1, canUpdate: true }])
+<script setup name="FieldOrder">
+import * as FieldApi from '@/api/clue/orderField.js'
+import { getDictOptions } from '@/utils/dict'
+
+const message = useMessage() // 消息弹窗
+const { t } = useI18n() // 国际化
+
+const tableList = ref([])
+const currentRowId = ref('')
+
+function setRowClass({ row }) {
+  return row.field == currentRowId.value ? 'current-row' : ''
+}
+
+function handleRowClick(row) {
+  formType.value = 'update'
+  currentRowId.value = row.field
+  form.value = { ...row }
+}
 
 const form = ref({
-  name: undefined,
-  type: undefined,
-  option: undefined
+  label: undefined,
+  field: '',
+  component: undefined,
+  options: [],
+  status: 0,
+  isCustom: true,
+  isForm: true,
+  isSearch: true,
+  isTable: true
 })
 
-const typeOptions = ref([
-  { label: '输入框', value: 'Input' },
-  { label: '多选', value: 'Checkbox' },
-  { label: '单选', value: 'Radio' },
-  { label: '下拉选', value: 'Select' },
-  { label: '开关', value: 'Switch' },
-  { label: '日期选择', value: 'DatePicker' },
-  { label: '时间选择', value: 'TimePicker' },
-  { label: '富文本', value: 'Editor' },
-  { label: '图片', value: 'UploadImg' },
-  { label: '文件', value: 'UploadFile' }
-])
+const loading = ref(false)
+
+const typeOptions = ref([])
 
-const rules = {}
+const rules = {
+  label: { required: true, message: '名称不可为空', trigger: 'blur' },
+  field: { required: true, message: '编码不可为空', trigger: 'blur' },
+  component: { required: true, message: '类型不可为空', trigger: 'change' }
+}
+
+async function getList() {
+  loading.value = true
+  try {
+    const data = await FieldApi.getDiyFieldList()
+    tableList.value = data
+    if (data.length) {
+      handleRowClick(data[0])
+    }
+  } finally {
+    loading.value = false
+  }
+}
 
-const optionList = ref([])
+const formType = ref('')
 
 function handleInsert() {
-  console.log('新增')
+  formType.value = 'create'
+  form.value = {
+    label: undefined,
+    field: '',
+    component: undefined,
+    options: [],
+    status: 0,
+    isCustom: true,
+    isForm: true,
+    isSearch: true,
+    isTable: true
+  }
 }
 
-function changeStatus(row) {
-  console.log(row.status)
+async function remove(id) {
+  try {
+    // 删除的二次确认
+    await message.delConfirm()
+    // 发起删除
+    await FieldApi.deleteField(id)
+    message.success(t('common.delSuccess'))
+    // 刷新列表
+    await getList()
+  } catch (err) {
+    console.log(err)
+  }
 }
 
-function remove(row) {
-  console.log(row.status)
+async function changeStatus(row) {
+  try {
+    // 二次确认
+    await message.confirm('是否确认修改状态')
+    await FieldApi.updateFieldStatus(row.clueParamId, row.status)
+    message.success('修改成功')
+    // 刷新列表
+    await getList()
+  } catch {
+    // 取消后,进行恢复按钮
+    row.status = row.status == 0 ? 1 : 0
+  }
 }
 
-function onSubmit() {
-  console.log('保存')
+const fieldForm = ref()
+const formLoading = ref(false)
+
+function handleAddOption() {
+  if (form.value.options) {
+    form.value.options.push({})
+  } else {
+    form.value.options = []
+  }
+}
+
+async function onSubmit() {
+  // 校验表单
+  if (!fieldForm.value) return
+  const valid = await fieldForm.value.validate()
+  if (!valid) return
+  // 提交请求
+  formLoading.value = true
+  try {
+    if (formType.value === 'create') {
+      await FieldApi.createField(form.value)
+      message.success(t('common.createSuccess'))
+    } else {
+      await FieldApi.updateField(form.value)
+      message.success(t('common.updateSuccess'))
+    }
+    // 发送操作成功的事件
+    getList()
+  } finally {
+    formLoading.value = false
+  }
 }
+
+onMounted(() => {
+  typeOptions.value = getDictOptions('attribute_type')
+  getList()
+})
 </script>
 
 <style lang="scss" scoped></style>
diff --git a/src/views/Clue/Set/index.vue b/src/views/Clue/Set/index.vue
index f50f702..9718b0d 100644
--- a/src/views/Clue/Set/index.vue
+++ b/src/views/Clue/Set/index.vue
@@ -1,26 +1,34 @@
 <template>
   <div>
     <el-tabs v-model="tabIndex" type="border-card">
-      <el-tab-pane label="线索属性" :name="0">
-        <FieldClue />
+      <el-tab-pane label="线索属性" :name="0" v-if="checkPermi(['clue:setting:clue-props'])">
+        <FieldClue v-if="tabIndex == 0" />
       </el-tab-pane>
-      <el-tab-pane label="成交属性" :name="10">
-        <FieldOrder />
+      <el-tab-pane label="成交属性" :name="10" v-if="checkPermi(['clue:setting:order-props'])">
+        <FieldOrder v-if="tabIndex == 10" />
       </el-tab-pane>
-      <el-tab-pane label="线索来源" :name="15">
-        <ClueSource />
+      <el-tab-pane label="线索来源" :name="15" v-if="checkPermi(['clue:setting:clue-resource'])">
+        <ClueSource v-if="tabIndex == 15" />
       </el-tab-pane>
-      <el-tab-pane label="线索获取规则" :name="20">
-        <ClueGet />
+      <!-- <el-tab-pane
+        label="线索获取规则"
+        :name="20"
+        v-if="checkPermi(['clue:setting:clue-get-rules'])"
+      >
+        <ClueGet v-if="tabIndex == 20" />
+      </el-tab-pane> -->
+      <!-- <el-tab-pane
+        label="线索分配规则"
+        :name="30"
+        v-if="checkPermi(['clue:setting:clue-send-rules'])"
+      >
+        <ClueSend v-if="tabIndex == 30" />
+      </el-tab-pane> -->
+      <el-tab-pane label="常规设置" :name="40" v-if="checkPermi(['clue:setting:general-setting'])">
+        <GeneralSet v-if="tabIndex == 40" />
       </el-tab-pane>
-      <el-tab-pane label="线索分配规则" :name="30">
-        <ClueSend />
-      </el-tab-pane>
-      <el-tab-pane label="常规设置" :name="40">
-        <GeneralSet />
-      </el-tab-pane>
-      <el-tab-pane label="消息通知" :name="50">
-        <MsgSend />
+      <el-tab-pane label="消息通知" :name="50" v-if="checkPermi(['mall:setting:prod'])">
+        <MsgSend v-if="tabIndex == 50" />
       </el-tab-pane>
     </el-tabs>
   </div>
@@ -30,10 +38,11 @@
 import FieldClue from './Comp/FieldClue.vue'
 import FieldOrder from './Comp/FieldOrder.vue'
 import ClueSource from './Comp/ClueSource.vue'
-import ClueGet from './Comp/ClueGet.vue'
-import ClueSend from './Comp/ClueSend.vue'
+// import ClueGet from './Comp/ClueGet.vue'
+// import ClueSend from './Comp/ClueSend.vue'
 import MsgSend from './Comp/MsgSend.vue'
 import GeneralSet from './Comp/GeneralSet.vue'
+import { checkPermi } from '@/utils/permission'
 
 const tabIndex = ref(0)
 </script>
diff --git a/src/views/Clue/Skill/index.vue b/src/views/Clue/Skill/index.vue
index f2c2a55..927e23f 100644
--- a/src/views/Clue/Skill/index.vue
+++ b/src/views/Clue/Skill/index.vue
@@ -11,9 +11,13 @@
         />
       </el-form-item>
       <el-form-item>
-        <el-button type="primary" @click="handleQuery">搜索</el-button>
-        <el-button @click="resetQuery">重置</el-button>
-        <el-button type="primary" plain @click="handleAdd">新增</el-button>
+        <el-button type="primary" @click="handleQuery" v-hasPermi="['clue:skill:search']">
+          搜索
+        </el-button>
+        <el-button @click="resetQuery" v-hasPermi="['clue:skill:reset']">重置</el-button>
+        <el-button type="primary" plain @click="handleAdd" v-hasPermi="['clue:skill:add']">
+          新增
+        </el-button>
       </el-form-item>
     </el-form>
     <el-table :data="tableList">
@@ -27,8 +31,22 @@
       <el-table-column label="关键词" align="center" prop="skillKey" />
       <el-table-column label="操作" align="center">
         <template #default="scope">
-          <el-button type="primary" text @click="handleUpdate(scope.row)">修改</el-button>
-          <el-button type="primary" text @click="handleDelete(scope.row)">删除</el-button>
+          <el-button
+            type="primary"
+            text
+            @click="handleUpdate(scope.row)"
+            v-hasPermi="['clue:skill:update']"
+          >
+            修改
+          </el-button>
+          <el-button
+            type="primary"
+            text
+            @click="handleDelete(scope.row)"
+            v-hasPermi="['clue:skill:delete']"
+          >
+            删除
+          </el-button>
         </template>
       </el-table-column>
     </el-table>
diff --git a/src/views/MiniMall/Inventory/Comp/InventoryRecord.vue b/src/views/MiniMall/Inventory/Comp/InventoryRecord.vue
index 5d458e8..fb42853 100644
--- a/src/views/MiniMall/Inventory/Comp/InventoryRecord.vue
+++ b/src/views/MiniMall/Inventory/Comp/InventoryRecord.vue
@@ -51,8 +51,8 @@
 
     <el-table class="mt-20px" :data="tableList" border>
       <el-table-column type="index" width="50px" />
-      <el-table-column prop="" label="产品名称" />
-      <el-table-column prop="" label="规格" />
+      <el-table-column prop="productName" label="产品名称" />
+      <el-table-column prop="specsName" label="规格" />
       <el-table-column label="变动类型">
         <template #default="{ row }">
           {{ ['', '入库', '出库'][row.changeType] }}
@@ -61,8 +61,8 @@
       <el-table-column prop="num" label="数量" />
       <el-table-column prop="money" label="金额" />
       <el-table-column prop="changeTime" label="变动时间" :formatter="dateFormatter" />
-      <el-table-column prop="changeUser" label="变动人员" />
-      <el-table-column prop="" label="所属仓库" />
+      <el-table-column prop="changeUserName" label="变动人员" />
+      <el-table-column prop="warehouseName" label="所属仓库" />
       <el-table-column prop="remark" label="备注" />
     </el-table>
     <Pagination
diff --git a/src/views/MiniMall/Inventory/Comp/Warehouse.vue b/src/views/MiniMall/Inventory/Comp/Warehouse.vue
index e5c78b6..d667f8f 100644
--- a/src/views/MiniMall/Inventory/Comp/Warehouse.vue
+++ b/src/views/MiniMall/Inventory/Comp/Warehouse.vue
@@ -25,12 +25,19 @@
           <el-button
             type="primary"
             style="padding: 0"
+            :disabled="row.type == 1"
             text
             @click="openForm('update', row.warehouseId)"
           >
             修改
           </el-button>
-          <el-button type="danger" style="padding: 0" text @click="handleRemove(row.warehouseId)">
+          <el-button
+            type="danger"
+            style="padding: 0"
+            :disabled="row.type == 1"
+            text
+            @click="handleRemove(row.warehouseId)"
+          >
             删除
           </el-button>
         </template>