Lijy 11 ヶ月 前
コミット
d53c639bd2

+ 3 - 3
fhKeeper/formulahousekeeper/customerBuler-crm/src/components/detailcompinents/relatedTasks.vue

@@ -15,11 +15,11 @@
                         }}</el-button>
                     </template>
                 </el-table-column>
-                <el-table-column prop="priorityStr" label="优先级" width="130" />
+                <el-table-column prop="priorityStr" sortable label="优先级" width="130" />
                 <el-table-column prop="statusStr" label="状态" width="130" />
                 <el-table-column prop="executorNamesStr" label="执行人" width="130" />
-                <el-table-column prop="startTimes" label="开始时间" width="130" />
-                <el-table-column prop="endTimes" label="截至时间" width="130" />
+                <el-table-column prop="startTimes" sortable label="开始时间" width="130" />
+                <el-table-column prop="endTimes" sortable label="截至时间" width="130" />
             </el-table>
         </div>
     </div>

+ 1 - 1
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/business/component/relatedTasks.vue

@@ -3,7 +3,7 @@
         <div class="flex justify-between">
             <div class="title">相关任务</div>
             <div>
-                <el-button type="primary">新建任务</el-button>
+                <el-button type="primary" v-permission="['tasksAdd']">新建任务</el-button>
             </div>
         </div>
         <div class="flex-1 overflow-auto pt-3">

+ 54 - 16
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/thread/detail/components/information.vue

@@ -3,54 +3,63 @@
         <div class="flex justify-between">
             <div class="title">基本信息</div>
             <div>
-                <el-button type="primary" @click="showVisible('newBusinessisible')">转为商机</el-button>
+                <el-button type="primary" v-permission="['threadEdit', userInfo.id == information.inchargerId]"
+                    @click="showVisible('newBusinessisible')">转为商机</el-button>
                 <el-button type="primary" @click="claimClues()" v-if="!information.inchargerName">认领</el-button>
                 <el-button type="primary" @click="showVisible('clueDialogVisible')" v-else>转移</el-button>
-                <el-button type="primary" @click="editClue(information)">编辑</el-button>
+                <el-button type="primary" @click="editClue(information)" v-permission="['tasksAdd']">编辑</el-button>
             </div>
         </div>
         <div class="form flex flex-wrap justify-between">
             <div class="formItem flex pt-3 pb-1">
                 <div class="w-20 text-right text-gray-500">线索名称:</div>
-                <div class="flex-1 overflow-hidden text-ellipsis whitespace-nowrap ml-1">{{ information.clueName }}</div>
+                <div class="flex-1 overflow-hidden text-ellipsis whitespace-nowrap ml-1" v-ellipsis-tooltip>{{
+                    information.clueName }}</div>
             </div>
             <div class="formItem flex pt-3 pb-1">
                 <div class="w-20 text-right text-gray-500">线索来源:</div>
-                <div class="flex-1 overflow-hidden text-ellipsis whitespace-nowrap ml-1">{{ information.clueSourceValue }}
+                <div class="flex-1 overflow-hidden text-ellipsis whitespace-nowrap ml-1" v-ellipsis-tooltip>{{
+                    information.clueSourceValue }}
                 </div>
             </div>
             <div class="formItem flex pt-3 pb-1">
                 <div class="w-20 text-right text-gray-500">电话号码:</div>
-                <div class="flex-1 overflow-hidden text-ellipsis whitespace-nowrap ml-1">{{ information.phone }}</div>
+                <div class="flex-1 overflow-hidden text-ellipsis whitespace-nowrap ml-1" v-ellipsis-tooltip>{{
+                    information.phone }}</div>
             </div>
             <div class="formItem flex pt-3 pb-1">
                 <div class="w-20 text-right text-gray-500">邮箱:</div>
-                <div class="flex-1 overflow-hidden text-ellipsis whitespace-nowrap ml-1">{{ information.email }}</div>
+                <div class="flex-1 overflow-hidden text-ellipsis whitespace-nowrap ml-1" v-ellipsis-tooltip>{{
+                    information.email }}</div>
             </div>
             <div class="formItem flex pt-3 pb-1">
                 <div class="w-20 text-right text-gray-500">客户行业:</div>
-                <div class="flex-1 overflow-hidden text-ellipsis whitespace-nowrap ml-1">{{
+                <div class="flex-1 overflow-hidden text-ellipsis whitespace-nowrap ml-1" v-ellipsis-tooltip>{{
                     information.customerIndustryValue
-                }}</div>
+                    }}</div>
             </div>
             <div class="formItem flex pt-3 pb-1">
                 <div class="w-20 text-right text-gray-500">客户级别:</div>
-                <div class="flex-1 overflow-hidden text-ellipsis whitespace-nowrap ml-1">{{ information.customerLevelValue
-                }}
+                <div class="flex-1 overflow-hidden text-ellipsis whitespace-nowrap ml-1" v-ellipsis-tooltip>{{
+                    information.customerLevelValue
+                    }}
                 </div>
             </div>
             <div class="formItem flex pt-3 pb-1">
                 <div class="w-20 text-right text-gray-500">客户地址:</div>
-                <div class="flex-1 overflow-hidden text-ellipsis whitespace-nowrap ml-1">{{ information.address }}</div>
+                <div class="flex-1 overflow-hidden text-ellipsis whitespace-nowrap ml-1" v-ellipsis-tooltip>{{
+                    information.address }}</div>
             </div>
             <div class="formItem flex pt-3 pb-1">
                 <div class="w-20 text-right text-gray-500">负责人:</div>
-                <div class="flex-1 overflow-hidden text-ellipsis whitespace-nowrap ml-1">{{ information.inchargerName }}
+                <div class="flex-1 overflow-hidden text-ellipsis whitespace-nowrap ml-1" v-ellipsis-tooltip>{{
+                    information.inchargerName }}
                 </div>
             </div>
             <div class="formItem flex pt-3 pb-1">
                 <div class="w-20 text-right text-gray-500">创建人:</div>
-                <div class="flex-1 overflow-hidden text-ellipsis whitespace-nowrap ml-1">{{ information.createName }}</div>
+                <div class="flex-1 overflow-hidden text-ellipsis whitespace-nowrap ml-1" v-ellipsis-tooltip>{{
+                    information.createName }}</div>
             </div>
             <div class="formItem flex pt-3 pb-1">
                 <div class="w-20 text-right text-gray-500">创建时间:</div>
@@ -59,7 +68,7 @@
             </div>
             <div class="formItem flex pt-3 pb-1" style="width: 100%;">
                 <div class="w-20 text-right text-gray-500">备注:</div>
-                <div class="flex-1 ml-1 text ">
+                <div class="flex-1 ml-1 text" v-ellipsis-tooltip>
                     {{ information.remark }}
                 </div>
             </div>
@@ -89,7 +98,8 @@
                 <div class="flex justify-between items-center border-b pb-3 dialog-header">
                     <h4 :id="titleId">{{ allText.clueText }}</h4>
                     <div>
-                        <el-button type="primary" :loading="allLoading.clueLoading" @click="transferClues()">转移</el-button>
+                        <el-button type="primary" :loading="allLoading.clueLoading"
+                            @click="transferClues()">转移</el-button>
                         <el-button @click="dialogVisible.clueDialogVisible = false">取消</el-button>
                     </div>
                 </div>
@@ -120,7 +130,7 @@
             <div class="h-[60vh] overflow-y-auto scroll-bar pt-3">
                 <GenerateForm ref="generateFormDataRef" :data="generateFormData" :value="generateFormVal" />
                 <div>相关产品</div>
-                <RelatedProducts ref="relatedProductsRef" :productTableList="productTableList" />
+                <RelatedProducts ref="relatedProductsRef" :productTableList="productTableList" :productTableListValue="{}" />
             </div>
         </el-dialog>
     </div>
@@ -137,6 +147,7 @@ import RelatedProducts from '@/components/relatedProducts/relatedProducts.vue'
 import { all } from 'axios';
 import { formatDateTime } from '@/utils/times';
 import { UPDATEINSET } from '@/pages/business/api';
+import { GETTABLELIST } from '@/pages/product/api';
 
 interface personnelInterface {
     id: string | number,
@@ -304,6 +315,32 @@ async function getSystemField() {
     })
 }
 
+function getProductTableList() {
+    post(GETTABLELIST, { pageIndex: -1, pageSize: -1 }).then((res) => {
+        if (res.code == 'ok') {
+            const { record, total } = res.data
+            productTableList.value = record.map((item: any) => {
+                const { id, productName, productCode, unit, unitName, typeName, type, price, inventory } = item
+                return {
+                    id,
+                    productId: id,
+                    productName,
+                    productCode,
+                    unit,
+                    unitName,
+                    price,
+                    type,
+                    typeName,
+                    inventory,
+                    quantity: '',
+                    discount: '',
+                    totalPrice: ''
+                }
+            })
+        }
+    })
+}
+
 watchEffect(() => {
     receiveAssignment(props)
 });
@@ -317,6 +354,7 @@ onMounted(async () => {
     let newData = JSON.parse(data.data[0].config)
     generateFormData.value = newData
     getSystemField()
+    getProductTableList()
 });
 </script>
 <style scoped lang="scss">

+ 3 - 3
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/thread/detail/components/operationRecord.vue

@@ -5,7 +5,7 @@
         </div>
         <div class="flex-1 overflow-auto pt-5">
             <el-table :data="operationRecordtable" border style="width: 100%;height: 100%;">
-                <el-table-column prop="creatTime" label="操作时间" width="140" />
+                <el-table-column prop="creatTime" label="操作时间" width="150" />
                 <el-table-column prop="userName" label="操作人" width="120" />
                 <el-table-column prop="name" label="操作内容" />
             </el-table>
@@ -13,7 +13,7 @@
     </div>
 </template>
 <script lang="ts" setup>
-import { formatDate } from '@/utils/tools';
+import { formatDateMinutes } from '@/utils/times';
 import { ref, onMounted, watchEffect } from 'vue'
 
 const props = defineProps<{
@@ -24,7 +24,7 @@ const operationRecordtable = ref([])
 
 watchEffect(() => {
     props.data.forEach((item: any) => {
-        item.creatTime = formatDate(new Date(item.creatTime))
+        item.creatTime = formatDateMinutes(new Date(item.creatTime))
     })
     operationRecordtable.value = props.data
 });

+ 6 - 6
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/thread/index.vue

@@ -52,12 +52,12 @@
     <div class="flex-1 p-5 overflow-auto">
       <div class="bg-white w-full h-full p-3 shadow-md rounded-md flex flex-col">
         <div class="flex justify-end pb-3">
-          <el-button type="primary" @click="editClue(false)">新建线索</el-button>
+          <el-button type="primary" v-permission="['threadAdd']" @click="editClue(false)">新建线索</el-button>
           <el-button type="primary" :disabled="batchTableData.length <= 0" @click="batchTransfer()">批量转移</el-button>
           <el-button type="primary" :disabled="batchTableData.length <= 0" @click="batchDeletes()">批量删除</el-button>
           <el-button type="primary" @click="showDeteleClue(true)">回收站</el-button>
-          <el-button type="primary" @click="dialogVisible.importVisible = true">导入</el-button>
-          <el-button type="primary" @click="exportTheadTableList()" :loading="allLoading.exoprtLoading">导出</el-button>
+          <el-button type="primary" v-permission="['threadImport']" @click="dialogVisible.importVisible = true">导入</el-button>
+          <el-button type="primary" v-permission="['threadExport']" @click="exportTheadTableList()" :loading="allLoading.exoprtLoading">导出</el-button>
         </div>
         <div class="flex-1 w-full overflow-hidden">
           <el-table :show-overflow-tooltip="tableShowOverflowTooltip" ref="clueTableRef" :data="clueTable" border v-loading="allLoading.clueTableLading"
@@ -78,11 +78,11 @@
             <el-table-column prop="createTime" label="创建时间" width="180"></el-table-column>
             <el-table-column label="操作" fixed="right" width="200">
               <template #default="scope">
-                <el-button link type="primary" size="large" @click="editClue(scope.row)">编辑</el-button>
+                <el-button link type="primary" size="large" @click="editClue(scope.row)" v-permission="['threadEdit']">编辑</el-button>
                 <!-- <el-button link type="primary" size="large"
                   @click="dialogVisible.taskModalVisible = true">新建任务</el-button> -->
-                <el-button link type="primary" size="large" @click="newTask(scope.row)">新建任务</el-button>
-                <el-button link type="danger" size="large" @click.prevent="deleteRow(scope.row)">删除</el-button>
+                <el-button link type="primary" size="large" @click="newTask(scope.row)" v-permission="['tasksAdd']">新建任务</el-button>
+                <el-button link type="danger" size="large" @click.prevent="deleteRow(scope.row)" v-permission="['threadEdit']">删除</el-button>
               </template>
             </el-table-column>
           </el-table>

+ 64 - 12
fhKeeper/formulahousekeeper/customerBuler-crm/src/utils/customInstructions.ts

@@ -1,22 +1,32 @@
-import { Directive, ObjectDirective } from 'vue';
+import { Directive, ObjectDirective, DirectiveBinding, render, createVNode, h } from 'vue';
+import { ElTooltip } from 'element-plus';
+import 'element-plus/theme-chalk/el-tooltip.css';
 
 // 权限控制
-const PermissionDirective: Directive = {
-    mounted(el: HTMLElement, binding: { value: string[] }, vnode: any) {
+const PermissionDirective: Directive = { // 数组, 权限 code 和 布尔值,
+    // mounted(el: HTMLElement, binding: { value: (string | boolean)[] }, vnode: any) {
+    updated(el: HTMLElement, binding: { value: (string | boolean)[] }, vnode: any) {
         const routePath: string = vnode.ctx.appContext.config.globalProperties.$route.path;
         const userInfo: { userInfo: { functionList: { code: string }[] } } | null = JSON.parse(localStorage.getItem('storeInfo') || '');
         const authorityCodes: string[] = (userInfo?.userInfo?.functionList || []).map(({ code }) => code);
-        const permissions: string[] = binding.value;
+        const permissions: (string | boolean)[] = binding.value;
+        const hasPermission: any[] = (binding.value || []).filter(item => typeof item !== 'boolean');
 
         if (!Array.isArray(permissions)) {
             console.error('权限必须以数组形式提供');
             return;
         }
 
-        if (!permissions.every(permission => authorityCodes.includes(permission))) {
+        console.log(binding.value)
+
+        if (permissions.some((element): element is boolean => element === true)) {
+            return;
+        }
+
+        if (!hasPermission.every(permission => authorityCodes.includes(permission))) {
             el.parentNode && el.parentNode.removeChild(el);
         }
-    }
+    },
 };
 
 // input 字符串数字
@@ -29,7 +39,50 @@ const PositiveIntegerDirective: ObjectDirective = {
     },
 };
 
-function handleInput(event: Event) {
+// 检测文字是否被省略并显示工具提示
+const EllipsisTooltipDirective: ObjectDirective = {
+    mounted(el: HTMLElement) {
+        const tooltipVNode = createVNode(ElTooltip, {
+            content: el.textContent,
+            placement: 'top',
+            effect: 'dark'
+        }, {
+            default: () => h('span', '')
+        });
+
+        const tooltipWrapper = document.createElement('div');
+        document.body.appendChild(tooltipWrapper);
+        render(tooltipVNode, tooltipWrapper);
+
+        el.addEventListener('mouseenter', (event: MouseEvent) => {
+            if (isTextEllipsis(el)) {
+                tooltipVNode.component!.props.content = el.textContent;
+                tooltipVNode.component!.props.visible = true;
+                const targetElement = event.currentTarget as HTMLElement;
+                const { clientWidth, clientHeight } = targetElement;
+                const { top, left } = targetElement.getBoundingClientRect();
+
+                tooltipWrapper.style.position = 'absolute';
+                tooltipWrapper.style.left = `${left + 10}px`;
+                tooltipWrapper.style.top = `${top}px`;
+                // tooltipWrapper.style.width = `${clientWidth}px`;
+                // tooltipWrapper.style.height = `${clientHeight}px`;
+            }
+        });
+
+        el.addEventListener('mouseleave', () => {
+            tooltipVNode.component!.props.visible = false;
+        });
+    },
+    unmounted(el: HTMLElement) {
+        el.removeEventListener('mouseenter', () => { });
+        el.removeEventListener('mouseleave', () => { });
+    }
+}
+
+
+// 对应方法
+function handleInput(event: Event) { // 处理输入
     const input = event.target as HTMLInputElement;
     const regex = /^\d*\.?\d*$/;
     if (!regex.test(input.value)) {
@@ -37,16 +90,15 @@ function handleInput(event: Event) {
     }
 }
 
-function extractPath(str: any) {
-    const startIndex = str.indexOf('/');
-    const endIndex = str.indexOf('/', startIndex + 1);
-    return str.slice(startIndex, endIndex !== -1 ? endIndex : undefined);
+function isTextEllipsis(element: HTMLElement): boolean { // 判断文字是否被省略
+    return element.scrollWidth > element.clientWidth || element.scrollHeight > element.clientHeight;
 }
 
 // 导出的自定义指令
 const customize = [
     { key: 'permission', directive: PermissionDirective, name: '角色权限' },
-    { key: 'enterNumber', directive: PositiveIntegerDirective, name: 'input正整数' }
+    { key: 'enterNumber', directive: PositiveIntegerDirective, name: 'input正整数' },
+    { key: 'ellipsisTooltip', directive: EllipsisTooltipDirective, name: '文字省略显示工具提示' },
 ]
 
 export default customize;