Forráskód Böngészése

Merge branch 'cheney' into dev

WanGxC 6 hónapja
szülő
commit
7196b74d14

+ 5 - 0
src/utils/emitter.ts

@@ -0,0 +1,5 @@
+import mitt from 'mitt'
+
+const emitter = mitt()
+
+export default emitter

+ 6 - 14
src/views/product-manage/Columns.ts

@@ -49,15 +49,15 @@ export const ProductColumns = [
     slots: { default: 'category' }
   },
   {
-    field: 'status', title: '状 态', width: 'auto', align: 'center',
+    field: 'status', title: '状 态', width: 80, align: 'center',
     slots: { default: 'status' }
   },
   {
-    field: 'update_datetime', title: '更新时间', minWidth: 'auto', align: 'center', showOverflow: true,
+    field: 'update_datetime', title: '更新时间', width: 160, align: 'center', 
     slots: { default: 'update_datetime' }
   },
   {
-    field: 'create_datetime', title: '创建时间', minWidth: 'auto', align: 'center', showOverflow: true,
+    field: 'create_datetime', title: '创建时间', width: 160, align: 'center',
     slots: { default: 'create_datetime' }
   },
   {
@@ -113,21 +113,13 @@ export const ProductMonitorColumns = [
     slots: { default: 'all_stars' }
   },
   {
-    field: 'price_info', title: '价 格',  width: 'auto', headerAlign: 'center', align: 'left',
+    field: 'price_info', title: '价 格', width: 'auto', align: 'left',
     slots: { default: 'price_info' }
   },
   {
-    field: 'show_price', title: '展示价格', width: 'auto', align: 'center',
+    field: 'show_price', title: '指导价格', width: 'auto', align: 'left',
     slots: { default: 'show_price' }
   },
-  {
-    field: 'activity_price', title: '平时活动售价', width: 'auto', align: 'center',
-    slots: { default: 'activity_price' }
-  },
-  {
-    field: 'minimum_price', title: '最低活动售价', width: 'auto', align: 'center',
-    slots: { default: 'minimum_price' }
-  },
   {
     field: 'ratings', title: '子ASIN评分人数', width: 'auto', align: 'center', sortable: true,
     slots: { default: 'ratings' }
@@ -210,7 +202,7 @@ export const CompetitorMonitorColumns = [
     slots: { default: 'all_stars' }
   },
   {
-    field: 'price_info', title: '价 格',  width: 'auto', headerAlign: 'center', align: 'left',
+    field: 'price_info', title: '价 格',  width: 'auto', align: 'left',
     slots: { default: 'price_info' }
   },
   {

+ 6 - 5
src/views/product-manage/comment-detail/component/AverageMonthly.vue

@@ -18,7 +18,7 @@ const { asin } = props;
 let chartRef: any = useTemplateRef('chartRef');
 let chart: echarts.ECharts | null = null;
 let resizeObserver: ResizeObserver | null = null;
-let chartData: any = [];
+let chartData: any = ref([]);
 
 onBeforeMount(() => {
   fetchChartData();
@@ -34,7 +34,7 @@ onUnmounted(() => {
 });
 
 function updateChart() {
-  if (!chart || chartData.length === 0) return;
+  if (!chart || chartData.value.length === 0) return;
 
   const chartOptions = {
     title: {
@@ -54,7 +54,7 @@ function updateChart() {
     },
     dataset: {
       dimensions: [ 'month', 'total', 'avg_score' ],
-      source: chartData
+      source: chartData.value
     },
     xAxis: {
       type: 'category'
@@ -152,7 +152,7 @@ function initChart() {
 async function fetchChartData() {
   const res = await useResponse(api.getChartData, { asin });
   if (res.code === 2000 && res.data) {
-    chartData = res.data;
+    chartData.value = res.data;
     updateChart();
   }
 }
@@ -160,7 +160,8 @@ async function fetchChartData() {
 
 <template>
   <el-card class="border-none" shadow="hover">
-    <div ref="chartRef" class="chart"></div>
+    <div v-show="chartData.length > 0" ref="chartRef" class="chart"></div>
+    <el-empty v-if="chartData.length == 0" description="暂无数据" class="chart" />
   </el-card>
 </template>
 

+ 2 - 1
src/views/product-manage/competitor-monitor/component/DataTableSlot.vue

@@ -145,7 +145,8 @@ function showDetail(detail: any) {
       </template>
     </div>
     <div v-else-if="field === 'stars'" class="flex flex-col font-normal" style="min-width: 170px">
-      <div v-for="(percent, index) in starsPercent(row.goods).reverse()" :key="index" class="w-full flex items-center">
+      <div v-for="(percent, index) in starsPercent(row.goods).reverse()" :key="index" class="w-full flex items-center"
+           style="max-height: 15px">
         <span class="w-10 text-right mr-2">{{ 5 - index }}星</span>
         <el-tooltip :content="String(row.goods[`ratings${5 - index}`])" :show-after="300" effect="dark" placement="top">
           <el-progress

+ 0 - 16
src/views/product-manage/historical-detail/component/DataTable.vue

@@ -5,7 +5,6 @@
  * @Author: Cheney
  */
 
-import { Refresh } from '@element-plus/icons-vue';
 import * as api from '../api';
 import { HistoricalColumns } from '/@/views/product-manage/Columns';
 import { usePagination } from '/@/utils/usePagination';
@@ -34,13 +33,6 @@ const gridOptions: any = reactive({
   customConfig: {
     storage: true
   },
-  toolbarConfig: {
-    size: 'large',
-    custom: true,
-    slots: {
-      tools: 'toolbar_tools'
-    }
-  },
   rowConfig: {
     isHover: true
   },
@@ -102,14 +94,6 @@ function handleRefresh() {
   <el-card class="border-none my-5">
     <vxe-grid ref="gridRef"
               v-bind="gridOptions">
-      <!-- 工具栏右侧 -->
-      <template #toolbar_tools>
-        <el-button circle class="toolbar-btn mr-2" @click="handleRefresh">
-          <el-icon>
-            <Refresh />
-          </el-icon>
-        </el-button>
-      </template>
       <template #top>
         <div class="mb-2"></div>
       </template>

+ 9 - 6
src/views/product-manage/historical-detail/component/PriceChart.vue

@@ -19,7 +19,8 @@ const { asin, country } = props;
 let chartRef: any = useTemplateRef('chartRef');
 let chart: echarts.ECharts | null = null;
 let resizeObserver: ResizeObserver | null = null;
-let chartData: any = [];
+let chartData: any = ref([]);
+const loading = ref(false)
 
 onBeforeMount(() => {
   fetchChartData();
@@ -35,7 +36,7 @@ onUnmounted(() => {
 });
 
 function updateChart() {
-  if (!chart || chartData.length === 0) return;
+  if (!chart || chartData.value.length === 0) return;
 
   const chartOptions = {
     title: {
@@ -55,7 +56,7 @@ function updateChart() {
     },
     dataset: {
       dimensions: [ 'create_datetime', 'new_val' ],
-      source: chartData
+      source: chartData.value
     },
     xAxis: {
       type: 'category'
@@ -120,9 +121,10 @@ function initChart() {
 }
 
 async function fetchChartData() {
-  const res = await useResponse(api.getChartData, { asin, country_code: country });
+  const res = await useResponse(api.getChartData, { asin, country_code: country }, loading);
   if (res.code === 2000 && res.data) {
-    chartData = res.data;
+    chartData.value = res.data;
+    console.log("(PriceChart.vue: 126)=> chartData", chartData);
     updateChart();
   }
 }
@@ -130,7 +132,8 @@ async function fetchChartData() {
 
 <template>
   <el-card class="border-none mt-2">
-    <div ref="chartRef" class="chart"></div>
+    <div v-show="chartData.length > 0" ref="chartRef" class="chart"></div>
+    <el-empty v-if="chartData.length == 0" description="暂无数据" class="chart" />
   </el-card>
 </template>
 

+ 1 - 1
src/views/product-manage/product-list/component/NoticeDialog.vue

@@ -126,7 +126,7 @@ function cancelDialog() {
           <span class="ml-1">仅可添加已绑定邮箱的用户</span>
         </el-col>
       </el-row>
-      <div class="flex flex-wrap gap-1.5 min-h-6">
+      <div class="flex flex-wrap gap-1.5 min-h-14">
         <el-tag
             v-for="tag in staffTags"
             :key="tag.id"

+ 13 - 11
src/views/product-manage/product-monitor/component/DataTableSlot.vue

@@ -79,19 +79,21 @@ function showDetail(detail: any) {
     </div>
     <div v-else-if="field === 'show_price'">
       <div class="font-medium">
-        {{ row.goods.show_price ? row.goods.currency_code + row.goods.show_price : '-' }}
-      </div>
-    </div>
-    <div v-else-if="field === 'activity_price'">
-      <div class="font-medium">
-        {{ row.goods.activity_price ? row.goods.currency_code + row.goods.activity_price : '-' }}
-      </div>
-    </div>
-    <div v-else-if="field === 'minimum_price'">
-      <div class="font-medium">
-        {{ row.goods.minimum_price ? row.goods.currency_code + row.goods.minimum_price : '-' }}
+        <p>展示价格:{{ row.goods.show_price ? row.goods.currency_code + row.goods.show_price : '-' }}</p>
+        <p>平时活动售价:{{ row.goods.activity_price ? row.goods.currency_code + row.goods.activity_price : '-' }}</p>
+        <p>最低活动售价:{{ row.goods.minimum_price ? row.goods.currency_code + row.goods.minimum_price : '-' }}</p>
       </div>
     </div>
+    <!--<div v-else-if="field === 'activity_price'">-->
+    <!--  <div class="font-medium">-->
+    <!--    {{ row.goods.activity_price ? row.goods.currency_code + row.goods.activity_price : '-' }}-->
+    <!--  </div>-->
+    <!--</div>-->
+    <!--<div v-else-if="field === 'minimum_price'">-->
+    <!--  <div class="font-medium">-->
+    <!--    {{ row.goods.minimum_price ? row.goods.currency_code + row.goods.minimum_price : '-' }}-->
+    <!--  </div>-->
+    <!--</div>-->
     <div v-else-if="field === 'score'">
       <template v-if="row.goods.score !== null && row.goods.score !== undefined && row.goods.score !== ''">
         <el-rate

+ 2 - 2
src/views/product-manage/product-monitor/component/ProgressBar.vue

@@ -18,8 +18,8 @@ const props = defineProps({
 </script>
 
 <template>
-  <div v-for="i in [5,4,3,2,1]" :key="i" class="w-full flex items-center">
-    <span class="w-10 text-right mr-2">{{ i }}星</span>
+  <div v-for="i in [5,4,3,2,1]" :key="i" class="w-full flex items-center" style="max-height: 15px;">
+    <span class="w-10 text-right mr-2 italic">{{ i }}星</span>
     <el-progress
         striped
         striped-flow

+ 2 - 2
src/views/product-manage/product-monitor/index.vue

@@ -79,7 +79,7 @@ function resetParameter() {
       <div ref="queryContainer" class="flex justify-between">
         <div class="flex flex-1">
           <div class="w-full whitespace-nowrap">
-            <el-row :gutter="20" style="margin-bottom: 16px;">
+            <el-row :gutter="20" style="margin-bottom: 10px;">
               <el-col :span="6">
                 <div class="flex items-center">
                   <span class="mr-2">国 家</span>
@@ -115,7 +115,7 @@ function resetParameter() {
                 </div>
               </el-col>
             </el-row>
-            <el-row :gutter="20" style="margin-bottom: 16px;">
+            <el-row :gutter="20" style="margin-bottom: 10px;">
               <el-col :span="6" class="flex">
                 <div class="flex items-center">
                   <span class="mr-2">店 铺</span>

+ 174 - 0
src/views/system/user/ShopAuthorize.vue

@@ -0,0 +1,174 @@
+<script lang="ts" setup>
+/**
+ * @Name: ShopAuthorize.vue
+ * @Description: 店铺授权
+ * @Author: Cheney
+ */
+import { ElMessage } from 'element-plus';
+import { useResponse } from '/@/utils/useResponse';
+import * as api from './api';
+import { Close, Finished } from '@element-plus/icons-vue';
+
+
+const props = defineProps({
+  rowData: Object
+});
+const { rowData } = props;
+
+const dialogVisible = defineModel({ default: false });
+
+const noticeDialog = <Ref>useTemplateRef('noticeDialog');
+
+const shopSelect = ref('');
+const shopOptions: any = ref([]);
+const shopTags: any = ref([]);
+const loading = ref(false);
+
+const radio = ref('1');
+
+onBeforeMount(async () => {
+  await fetchShopOptions();
+  await fetchAuthorizedShop();
+});
+
+function handleClose(done: any) {
+  shopSelect.value = '';
+  shopTags.value = [];
+  done();
+}
+
+function addShopChange() {
+  const selectedOption: any = shopOptions.value.find((option: any) => option.id === shopSelect.value);
+  if (selectedOption && !shopTags.value.some((tag: any) => tag.id === selectedOption.id)) {
+    // 如果选中的项不在 shopTags 中,则添加到 shopTags
+    shopTags.value.push({
+      id: selectedOption.id,
+      name: selectedOption.name
+    });
+  }
+}
+
+function isOptionDisabled(id: any) {
+  return shopTags.value.some((tag: any) => tag.id === id);
+}
+
+function removeTag(tag: any) {
+  shopTags.value = shopTags.value.filter((t: any) => t.id !== tag.id);
+  shopSelect.value = '';
+}
+
+async function fetchShopOptions() {
+  const res = await useResponse(api.getShopOptions);
+  shopOptions.value = res.data;
+}
+
+async function fetchAuthorizedShop() {
+  const resp = await useResponse(api.getAuthorizedShop, { user_id: rowData?.id });
+  const authorizedShops = (resp.data.authorized_shops ?? []).filter((item: any) => item !== null); // 过滤掉 null 值
+
+  if (resp.data.is_authorized_all_shops) {
+    radio.value = '2';
+  } else if (authorizedShops.length > 0) {
+    radio.value = '1';
+    shopTags.value = authorizedShops.map((id: string) => {
+      const selectedOption = shopOptions.value.find((option: any) => option.id === id);
+      return {
+        id: selectedOption.id,
+        name: selectedOption.name
+      };
+    });
+  }
+}
+
+async function confirmAuthorize() {
+  const body = {
+    ...(radio.value === '2' ? {} : { authorized_shops: shopTags.value.map((tag: any) => tag.id) }),
+    user_id: rowData?.id,
+    is_authorized_all_shops: radio.value === '2'
+  };
+  try {
+    const resp = await useResponse(api.postAuthorize, body, loading);
+    if (resp.code === 2000) {
+      ElMessage.success('编辑成功!');
+      fetchAuthorizedShop();
+    }
+  } catch (error) {
+    ElMessage.error('编辑失败!');
+  } finally {
+    shopSelect.value = '';
+  }
+}
+
+function cancelDialog() {
+  shopSelect.value = '';
+  shopTags.value = [];
+  noticeDialog.value.visible = false;
+}
+
+function radioChange(val: any) {
+  if (val == 2) {
+    shopSelect.value = '';
+  }
+}
+
+</script>
+
+<template>
+  <div>
+    <el-dialog
+        ref="noticeDialog"
+        v-model="dialogVisible"
+        :before-close="handleClose"
+        :close-on-click-modal="false"
+        :close-on-press-escape="false"
+        title="店铺授权"
+        width="40%"
+    >
+      <el-row class="mb-2">
+        <el-col>
+          <el-radio-group v-model="radio" @change="radioChange">
+            <el-radio value="1"><span class="mr-2 font-medium">店 铺</span>
+              <el-select v-model="shopSelect" :disabled="radio=='2'" filterable placeholder="输入搜索"
+                         style="width: 200px;"
+                         @change="addShopChange">
+                <el-option
+                    v-for="item in shopOptions"
+                    :key="item.id"
+                    :disabled="isOptionDisabled(item.id)"
+                    :label="item.name"
+                    :value="item.id">
+                </el-option>
+              </el-select>
+            </el-radio>
+            <el-radio value="2">所有店铺</el-radio>
+          </el-radio-group>
+        </el-col>
+      </el-row>
+      <el-row :gutter="20" class="mb-4">
+        <el-col :span="2">
+        </el-col>
+      </el-row>
+      <div class="flex flex-wrap gap-1.5 min-h-14">
+        <el-tag
+            v-for="tag in shopTags"
+            v-show="radio=='1'"
+            :key="tag.id"
+            closable
+            effect="plain"
+            round
+            @close="removeTag(tag)">
+          {{ tag.name }}
+        </el-tag>
+      </div>
+      <el-divider style="margin: 12px 0 20px 0"></el-divider>
+      <template #footer>
+        <el-button :icon="Close" @click="cancelDialog">取 消</el-button>
+        <el-button :icon="Finished" :loading="loading" type="primary" @click="confirmAuthorize">确 定</el-button>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<style scoped>
+
+</style>

+ 24 - 0
src/views/system/user/api.ts

@@ -64,3 +64,27 @@ export function resetToDefaultPassword(id:any){
         method: 'put'
     })
 }
+
+export function getShopOptions(query: any) {
+    return request({
+        url: '/api/choice/marketplace_shops/select/',
+        method: 'GET',
+        params: query
+    })
+}
+
+export function getAuthorizedShop(query: any) {
+    return request({
+        url: '/api/system/user/authorized-shop/',
+        method: 'GET',
+        params: query
+    })
+}
+
+export function postAuthorize(body: any) {
+    return request({
+        url: '/api/system/user/authorized-shop/',
+        method: 'POST',
+        data: body
+    })
+}

+ 396 - 381
src/views/system/user/crud.tsx

@@ -1,395 +1,410 @@
 import * as api from './api';
 import {
-    dict,
-    UserPageQuery,
-    AddReq,
-    DelReq,
-    EditReq,
-    compute,
-    CreateCrudOptionsProps,
-    CreateCrudOptionsRet
+  AddReq,
+  compute,
+  CreateCrudOptionsProps,
+  CreateCrudOptionsRet,
+  DelReq,
+  dict,
+  EditReq,
+  UserPageQuery
 } from '@fast-crud/fast-crud';
-import {request} from '/@/utils/service';
-import {dictionary} from '/@/utils/dictionary';
-import {successMessage} from '/@/utils/message';
-import {auth} from '/@/utils/authFunction';
-import {SystemConfigStore} from "/@/stores/systemConfig";
-import {storeToRefs} from "pinia";
-import {computed} from "vue";
+import { dictionary } from '/@/utils/dictionary';
+import { successMessage } from '/@/utils/message';
+import { auth } from '/@/utils/authFunction';
+import { SystemConfigStore } from '/@/stores/systemConfig';
+import { storeToRefs } from 'pinia';
+import { computed } from 'vue';
 import { Md5 } from 'ts-md5';
-import {commonCrudConfig} from "/@/utils/commonCrud";
-export const createCrudOptions = function ({crudExpose}: CreateCrudOptionsProps): CreateCrudOptionsRet {
-    const pageRequest = async (query: UserPageQuery) => {
-        return await api.GetList(query);
-    };
-    const editRequest = async ({form, row}: EditReq) => {
-        form.id = row.id;
-        return await api.UpdateObj(form);
-    };
-    const delRequest = async ({row}: DelReq) => {
-        return await api.DelObj(row.id);
-    };
-    const addRequest = async ({form}: AddReq) => {
-        return await api.AddObj(form);
-    };
+import { commonCrudConfig } from '/@/utils/commonCrud';
+import emitter from '/@/utils/emitter';
 
-    const exportRequest = async (query: UserPageQuery) => {
-        return await api.exportData(query)
-    }
 
-    const resetToDefaultPasswordRequest = async (row:EditReq)=>{
-        await api.resetToDefaultPassword(row.id)
-        successMessage("重置密码成功")
-    }
+export const createCrudOptions = function({ crudExpose }: CreateCrudOptionsProps): CreateCrudOptionsRet {
+  const pageRequest = async (query: UserPageQuery) => {
+    return await api.GetList(query);
+  };
+  const editRequest = async ({ form, row }: EditReq) => {
+    form.id = row.id;
+    return await api.UpdateObj(form);
+  };
+  const delRequest = async ({ row }: DelReq) => {
+    return await api.DelObj(row.id);
+  };
+  const addRequest = async ({ form }: AddReq) => {
+    return await api.AddObj(form);
+  };
 
-    const systemConfigStore = SystemConfigStore()
-    const {systemConfig} = storeToRefs(systemConfigStore)
-    const getSystemConfig = computed(() => {
-        // console.log(systemConfig.value)
-        return systemConfig.value
-    })
+  const exportRequest = async (query: UserPageQuery) => {
+    return await api.exportData(query);
+  };
 
+  const resetToDefaultPasswordRequest = async (row: EditReq) => {
+    await api.resetToDefaultPassword(row.id);
+    successMessage('重置密码成功');
+  };
 
-    return {
-        crudOptions: {
-            table: {
-                remove: {
-                    confirmMessage: '是否删除该用户?',
-                },
-            },
-            request: {
-                pageRequest,
-                addRequest,
-                editRequest,
-                delRequest,
-            },
-            form: {
-                initialForm: {
-                    password: computed(() => {
-                        return systemConfig.value['base.default_password']
-                    }),
-                }
-            },
-            actionbar: {
-                buttons: {
-                    add: {
-                        show: auth('user:Create')
-                    },
-                    export: {
-                        text: "导出",//按钮文字
-                        title: "导出",//鼠标停留显示的信息
-                        show: auth('user:Export'),
-                        click() {
-                            return exportRequest(crudExpose!.getSearchFormData())
-                        }
-                    }
-                }
+  const systemConfigStore = SystemConfigStore();
+  const { systemConfig } = storeToRefs(systemConfigStore);
+  const getSystemConfig = computed(() => {
+    // console.log(systemConfig.value)
+    return systemConfig.value;
+  });
+
+  return {
+    crudOptions: {
+      table: {
+        remove: {
+          confirmMessage: '是否删除该用户?'
+        }
+      },
+      request: {
+        pageRequest,
+        addRequest,
+        editRequest,
+        delRequest
+      },
+      form: {
+        initialForm: {
+          password: computed(() => {
+            return systemConfig.value['base.default_password'];
+          })
+        }
+      },
+      actionbar: {
+        buttons: {
+          add: {
+            show: auth('user:Create')
+          },
+          export: {
+            text: '导出',//按钮文字
+            title: '导出',//鼠标停留显示的信息
+            show: auth('user:Export'),
+            click() {
+              return exportRequest(crudExpose!.getSearchFormData());
+            }
+          }
+        }
+      },
+      rowHandle: {
+        //固定右侧
+        fixed: 'right',
+        width: 150,
+        buttons: {
+          view: {
+            show: false
+          },
+          edit: {
+            iconRight: 'Edit',
+            type: 'text',
+            show: auth('user:Update')
+          },
+          remove: {
+            iconRight: 'Delete',
+            type: 'text',
+            show: auth('user:Delete')
+          },
+          custom: {
+            text: '重设密码',
+            type: 'text',
+            show: auth('user:ResetPassword'),
+            tooltip: {
+              placement: 'top',
+              content: '重设密码'
             },
-            rowHandle: {
-                //固定右侧
-                fixed: 'right',
-                width: 200,
-                buttons: {
-                    view: {
-                        show: false,
-                    },
-                    edit: {
-                        iconRight: 'Edit',
-                        type: 'text',
-                        show: auth('user:Update'),
-                    },
-                    remove: {
-                        iconRight: 'Delete',
-                        type: 'text',
-                        show: auth('user:Delete'),
-                    },
-                    custom: {
-                        text: '重设密码',
-                        type: 'text',
-                        show: auth('user:ResetPassword'),
-                        tooltip: {
-                            placement: 'top',
-                            content: '重设密码',
-                        },
-                        //@ts-ignore
-                        click: (ctx: any) => {
-                            const {row} = ctx;
-                            resetToDefaultPasswordRequest(row)
-                        },
-                    },
-                },
+            //@ts-ignore
+            click: (ctx: any) => {
+              const { row } = ctx;
+              resetToDefaultPasswordRequest(row);
+            }
+          },
+          custom2: {
+            text: '店铺授权',
+            type: 'text',
+            // show: auth('user:ResetPassword'),
+            tooltip: {
+              placement: 'top',
+              content: '店铺授权'
             },
-            columns: {
-                _index: {
-                    title: '序号',
-                    form: {show: false},
-                    column: {
-                        type: 'index',
-                        align: 'center',
-                        width: '70px',
-                        columnSetDisabled: true, //禁止在列设置中选择
-                    },
-                },
-                username: {
-                    title: '账号',
-                    search: {
-                        show: true,
-                    },
-                    type: 'input',
-                    column: {
-                        minWidth: 100, //最小列宽
-                    },
-                    form: {
-                        rules: [
-                            // 表单校验规则
-                            {
-                                required: true,
-                                message: '账号必填项',
-                            },
-                        ],
-                        component: {
-                            placeholder: '请输入账号',
-                        },
-                    },
-                },
-                password: {
-                    title: '密码',
-                    type: 'password',
-                    column: {
-                        show: false,
-                    },
-                    editForm: {
-                        show: false,
-                    },
-                    form: {
-                        rules: [
-                            // 表单校验规则
-                            {
-                                required: true,
-                                message: '密码必填项',
-                            },
-                        ],
-                        component: {
+            //@ts-ignore
+            click: (ctx: any) => {
+              const { row } = ctx;
+              emitter.emit('shop-authorize', row);
+            }
+          }
+        }
+      },
+      columns: {
+        _index: {
+          title: '序号',
+          form: { show: false },
+          column: {
+            type: 'index',
+            align: 'center',
+            width: '70px',
+            columnSetDisabled: true //禁止在列设置中选择
+          }
+        },
+        username: {
+          title: '账号',
+          search: {
+            show: true
+          },
+          type: 'input',
+          column: {
+            minWidth: 100 //最小列宽
+          },
+          form: {
+            rules: [
+              // 表单校验规则
+              {
+                required: true,
+                message: '账号必填项'
+              }
+            ],
+            component: {
+              placeholder: '请输入账号'
+            }
+          }
+        },
+        password: {
+          title: '密码',
+          type: 'password',
+          column: {
+            show: false
+          },
+          editForm: {
+            show: false
+          },
+          form: {
+            rules: [
+              // 表单校验规则
+              {
+                required: true,
+                message: '密码必填项'
+              }
+            ],
+            component: {
 
-                            span: 12,
-                            showPassword: true,
-                            placeholder: '请输入密码',
-                        },
-                    },
-                    valueResolve({form}) {
-                        if (form.password) {
-                            form.password = Md5.hashStr(form.password)
-                        }
-                    }
-                },
-                name: {
-                    title: '姓名',
-                    search: {
-                        show: true,
-                    },
-                    type: 'input',
-                    column: {
-                        minWidth: 100, //最小列宽
-                    },
-                    form: {
-                        rules: [
-                            // 表单校验规则
-                            {
-                                required: true,
-                                message: '姓名必填项',
-                            },
-                        ],
-                        component: {
-                            span: 12,
-                            placeholder: '请输入姓名',
-                        },
-                    },
-                },
-                dept: {
-                    title: '部门',
-                    search: {
-                        disabled: true,
-                    },
-                    type: 'dict-tree',
-                    dict: dict({
-                        isTree: true,
-                        url: '/api/system/dept/all_dept/',
-                        value: 'id',
-                        label: 'name'
-                    }),
-                    column: {
-                        minWidth: 150, //最小列宽
-                    },
-                    form: {
-                        rules: [
-                            // 表单校验规则
-                            {
-                                required: true,
-                                message: '必填项',
-                            },
-                        ],
-                        component: {
-                            filterable: true,
-                            placeholder: '请选择',
-                            props: {
-                                checkStrictly:true,
-                                props: {
-                                    value: 'id',
-                                    label: 'name',
-                                },
-                            },
-                        },
-                    },
-                },
-                role: {
-                    title: '角色',
-                    search: {
-                        disabled: true,
-                    },
-                    type: 'dict-select',
-                    dict: dict({
-                        url: '/api/system/role/',
-                        value: 'id',
-                        label: 'name',
-                    }),
-                    column: {
-                        minWidth: 100, //最小列宽
-                    },
-                    form: {
-                        rules: [
-                            // 表单校验规则
-                            {
-                                required: true,
-                                message: '必填项',
-                            },
-                        ],
-                        component: {
-                            multiple: true,
-                            filterable: true,
-                            placeholder: '请选择角色',
-                        },
-                    },
-                },
-                mobile: {
-                    title: '手机号码',
-                    search: {
-                        show: true,
-                    },
-                    type: 'input',
-                    column: {
-                        minWidth: 120, //最小列宽
-                    },
-                    form: {
-                        rules: [
-                            {
-                                max: 20,
-                                message: '请输入正确的手机号码',
-                                trigger: 'blur',
-                            },
-                            {
-                                pattern: /^1[3-9]\d{9}$/,
-                                message: '请输入正确的手机号码',
-                            },
-                        ],
-                        component: {
-                            placeholder: '请输入手机号码',
-                        },
-                    },
-                },
-                email: {
-                    title: '邮箱',
-                    column: {
-                        width: 260,
-                    },
-                    form: {
-                        rules: [
-                            {
-                                type: 'email',
-                                message: '请输入正确的邮箱地址',
-                                trigger: ['blur', 'change'],
-                            },
-                        ],
-                        component: {
-                            placeholder: '请输入邮箱',
-                        },
-                    },
-                },
-                gender: {
-                    title: '性别',
-                    type: 'dict-select',
-                    dict: dict({
-                        data: dictionary('gender'),
-                    }),
-                    form: {
-                        value: 1,
-                        component: {
-                            span: 12,
-                        },
-                    },
-                    component: {props: {color: 'auto'}}, // 自动染色
-                },
-                user_type: {
-                    title: '用户类型',
-                    search: {
-                        show: true,
-                    },
-                    type: 'dict-select',
-                    dict: dict({
-                        data: dictionary('user_type'),
-                    }),
-                    column: {
-                        minWidth: 100, //最小列宽
-                    },
-                    form: {
-                        show: false,
-                        value: 0,
-                        component: {
-                            span: 12,
-                        },
-                    },
-                },
-                is_active: {
-                    title: '状态',
-                    search: {
-                        show: true,
-                    },
-                    type: 'dict-radio',
-                    column: {
-                        component: {
-                            name: 'fs-dict-switch',
-                            activeText: '',
-                            inactiveText: '',
-                            style: '--el-switch-on-color: var(--el-color-primary); --el-switch-off-color: #dcdfe6',
-                            onChange: compute((context) => {
-                                return () => {
-                                    api.UpdateObj(context.row).then((res: APIResponseData) => {
-                                        successMessage(res.msg as string);
-                                    });
-                                };
-                            }),
-                        },
-                    },
-                    dict: dict({
-                        data: dictionary('button_status_bool'),
-                    }),
-                },
-                avatar: {
-                    title: '头像',
-                    type: 'avatar-cropper',
-                    form: {
-                        show: false,
-                    },
-                    column: {
-                        minWidth: 400, //最小列宽
-                    },
-                },
-                ...commonCrudConfig({
-                    dept_belong_id: {
-                        form: true,
-                        table: true
-                    }
-                })
-            },
+              span: 12,
+              showPassword: true,
+              placeholder: '请输入密码'
+            }
+          },
+          valueResolve({ form }) {
+            if (form.password) {
+              form.password = Md5.hashStr(form.password);
+            }
+          }
         },
-    };
+        name: {
+          title: '姓名',
+          search: {
+            show: true
+          },
+          type: 'input',
+          column: {
+            minWidth: 100 //最小列宽
+          },
+          form: {
+            rules: [
+              // 表单校验规则
+              {
+                required: true,
+                message: '姓名必填项'
+              }
+            ],
+            component: {
+              span: 12,
+              placeholder: '请输入姓名'
+            }
+          }
+        },
+        dept: {
+          title: '部门',
+          search: {
+            disabled: true
+          },
+          type: 'dict-tree',
+          dict: dict({
+            isTree: true,
+            url: '/api/system/dept/all_dept/',
+            value: 'id',
+            label: 'name'
+          }),
+          column: {
+            minWidth: 150 //最小列宽
+          },
+          form: {
+            rules: [
+              // 表单校验规则
+              {
+                required: true,
+                message: '必填项'
+              }
+            ],
+            component: {
+              filterable: true,
+              placeholder: '请选择',
+              props: {
+                checkStrictly: true,
+                props: {
+                  value: 'id',
+                  label: 'name'
+                }
+              }
+            }
+          }
+        },
+        role: {
+          title: '角色',
+          search: {
+            disabled: true
+          },
+          type: 'dict-select',
+          dict: dict({
+            url: '/api/system/role/',
+            value: 'id',
+            label: 'name'
+          }),
+          column: {
+            minWidth: 100 //最小列宽
+          },
+          form: {
+            rules: [
+              // 表单校验规则
+              {
+                required: true,
+                message: '必填项'
+              }
+            ],
+            component: {
+              multiple: true,
+              filterable: true,
+              placeholder: '请选择角色'
+            }
+          }
+        },
+        mobile: {
+          title: '手机号码',
+          search: {
+            show: true
+          },
+          type: 'input',
+          column: {
+            minWidth: 120 //最小列宽
+          },
+          form: {
+            rules: [
+              {
+                max: 20,
+                message: '请输入正确的手机号码',
+                trigger: 'blur'
+              },
+              {
+                pattern: /^1[3-9]\d{9}$/,
+                message: '请输入正确的手机号码'
+              }
+            ],
+            component: {
+              placeholder: '请输入手机号码'
+            }
+          }
+        },
+        email: {
+          title: '邮箱',
+          column: {
+            width: 260
+          },
+          form: {
+            rules: [
+              {
+                type: 'email',
+                message: '请输入正确的邮箱地址',
+                trigger: [ 'blur', 'change' ]
+              }
+            ],
+            component: {
+              placeholder: '请输入邮箱'
+            }
+          }
+        },
+        gender: {
+          title: '性别',
+          type: 'dict-select',
+          dict: dict({
+            data: dictionary('gender')
+          }),
+          form: {
+            value: 1,
+            component: {
+              span: 12
+            }
+          },
+          component: { props: { color: 'auto' } } // 自动染色
+        },
+        user_type: {
+          title: '用户类型',
+          search: {
+            show: true
+          },
+          type: 'dict-select',
+          dict: dict({
+            data: dictionary('user_type')
+          }),
+          column: {
+            minWidth: 100 //最小列宽
+          },
+          form: {
+            show: false,
+            value: 0,
+            component: {
+              span: 12
+            }
+          }
+        },
+        is_active: {
+          title: '状态',
+          search: {
+            show: true
+          },
+          type: 'dict-radio',
+          column: {
+            component: {
+              name: 'fs-dict-switch',
+              activeText: '',
+              inactiveText: '',
+              style: '--el-switch-on-color: var(--el-color-primary); --el-switch-off-color: #dcdfe6',
+              onChange: compute((context) => {
+                return () => {
+                  api.UpdateObj(context.row).then((res: APIResponseData) => {
+                    successMessage(res.msg as string);
+                  });
+                };
+              })
+            }
+          },
+          dict: dict({
+            data: dictionary('button_status_bool')
+          })
+        },
+        avatar: {
+          title: '头像',
+          type: 'avatar-cropper',
+          form: {
+            show: false
+          },
+          column: {
+            minWidth: 400 //最小列宽
+          }
+        },
+        ...commonCrudConfig({
+          dept_belong_id: {
+            form: true,
+            table: true
+          }
+        })
+      }
+    }
+  };
 };

+ 16 - 6
src/views/system/user/index.vue

@@ -7,22 +7,22 @@
             部门列表
             <el-tooltip :content="content" effect="dark" placement="right">
               <el-icon>
-                <QuestionFilled/>
+                <QuestionFilled />
               </el-icon>
             </el-tooltip>
           </p>
-          <el-input v-model="filterText" :placeholder="placeholder"/>
+          <el-input v-model="filterText" :placeholder="placeholder" />
           <el-tree ref="treeRef" :data="data" :filter-node-method="filterNode" :indent="38"
                    :props="treeProps" class="font-mono font-bold leading-6 text-7xl" highlight-current icon="ArrowRightBold"
                    @node-click="onTreeNodeClick">
             <template #default="{ node, data }">
               <element-tree-line :indent="32" :node="node" :showLabelLine="false">
 					<span v-if="data.status" class="text-center font-black font-normal">
-						<SvgIcon color="var(--el-color-primary)" name="iconfont icon-shouye"/>&nbsp;{{ node.label }}
+						<SvgIcon color="var(--el-color-primary)" name="iconfont icon-shouye" />&nbsp;{{ node.label }}
 					</span>
-                <span v-else color="var(--el-color-primary)"> <SvgIcon name="iconfont icon-shouye"/>&nbsp;{{
+                <span v-else color="var(--el-color-primary)"> <SvgIcon name="iconfont icon-shouye" />&nbsp;{{
                     node.label
-                                                                                                    }} </span>
+                                                                                                     }} </span>
               </element-tree-line>
             </template>
           </el-tree>
@@ -38,7 +38,7 @@
         </el-card>
       </el-col>
     </el-row>
-
+    <ShopAuthorize v-if="dialogVisible" v-model="dialogVisible" :rowData="rowData" />
   </fs-page>
 </template>
 
@@ -51,6 +51,8 @@ import { ref, onMounted, watch, toRaw, h } from 'vue';
 import XEUtils from 'xe-utils';
 import { getElementLabelLine } from 'element-tree-line';
 import importExcel from '/@/components/importExcel/index.vue';
+import emitter from '/@/utils/emitter';
+import ShopAuthorize from '/@/views/system/user/ShopAuthorize.vue';
 
 
 const ElementTreeLine = getElementLabelLine(h);
@@ -133,6 +135,14 @@ const { resetCrudOptions } = useCrud({ crudExpose, crudOptions });
 onMounted(() => {
   crudExpose.doRefresh();
 });
+
+const dialogVisible = ref(false);
+const rowData = ref({});
+
+emitter.on('shop-authorize', (data: any) => {
+  dialogVisible.value = true;
+  rowData.value = data;
+})
 </script>
 
 <style lang="scss" scoped>