فهرست منبع

feat(product): add product page

gemercheung 3 سال پیش
والد
کامیت
062a1160e1

+ 51 - 0
mock/product/list.ts

@@ -0,0 +1,51 @@
+import { MockMethod } from 'vite-plugin-mock';
+import { mock, Random } from 'mockjs';
+import { resultPageSuccess } from '../_util';
+Random.extend({
+  phone: function () {
+    const phonePrefixs = ['132', '135', '189']; // 自己写前缀哈
+    return this.pick(phonePrefixs) + mock(/\d{8}/); //Number()
+  },
+});
+// console.log(Random.phone());
+// 生成 1 - 10 个 随机手机号码
+
+const demoList = (() => {
+  const result: any[] = [];
+  for (let index = 0; index < 200; index++) {
+    // const { phone } = mock({
+    //   phone: '@phone',
+    // });
+
+    result.push({
+      id: `${index}`,
+      name: '@ctitle(3,10)',
+      desc: '@cparagraph(3, 5)',
+      link: `https://zfb.4dkankan.com/smobile.html?m=@string( 'lower/number',5,10)`,
+      'productType|1': [0, 1, 2, 3],
+      steamRoom: {
+        id: 1,
+        name: '李嘉琪的直播间',
+      },
+      'amount|1': '@integer(20,50)',
+      'total|1': '@integer(20,50)',
+      'marketingUnit|1': '@float(20,50,3,4)',
+      'unit|1': '@float(20,50,3,6)',
+      isLaunched: '@boolean(1, 9, true)',
+      createTime: '@datetime',
+    });
+  }
+  return result;
+})();
+
+export default [
+  {
+    url: '/basic-api/zfb/product/list',
+    timeout: 1000,
+    method: 'get',
+    response: ({ query }) => {
+      const { page = 1, pageSize = 20 } = query;
+      return resultPageSuccess(page, pageSize, demoList);
+    },
+  },
+] as MockMethod[];

+ 21 - 0
src/api/product/list.ts

@@ -0,0 +1,21 @@
+import { defHttp } from '/@/utils/http/axios';
+import { PageParams, RentListGetResultModel } from './model';
+
+enum Api {
+  pageList = '/zfb/product/list',
+  category = '/zfb/product/category',
+}
+
+/**
+ * @description: Get sample list value
+ */
+
+export const ListApi = (params: PageParams) =>
+  defHttp.get<RentListGetResultModel>({
+    url: Api.pageList,
+    params,
+    headers: {
+      // @ts-ignore
+      ignoreCancelToken: true,
+    },
+  });

+ 19 - 0
src/api/product/model.ts

@@ -0,0 +1,19 @@
+import { BasicPageParams, BasicFetchResult } from '/@/api/model/baseModel';
+/**
+ * @description: Request list interface parameters
+ */
+export type PageParams = BasicPageParams;
+
+export interface OrderListItem {
+  id: number;
+  name: string;
+  image: string;
+  link: string;
+  createTime: string;
+  isShow: boolean;
+}
+
+/**
+ * @description: Request list return value
+ */
+export type RentListGetResultModel = BasicFetchResult<OrderListItem>;

+ 114 - 0
src/views/dashboard/product/drawer.data.ts

@@ -0,0 +1,114 @@
+import { FormSchema, BasicColumn } from '/@/components/Table';
+
+import { h } from 'vue';
+import { Switch } from 'ant-design-vue';
+import { useMessage } from '/@/hooks/web/useMessage';
+
+export const formSchema: FormSchema[] = [
+  {
+    field: 'name',
+    label: '商品名称',
+    component: 'Input',
+  },
+  {
+    field: 'desc',
+    label: '商品描述',
+    component: 'Input',
+  },
+  {
+    field: 'link',
+    label: '购买链接',
+    component: 'Input',
+  },
+];
+export const columns: BasicColumn[] = [
+  {
+    title: 'ID',
+    dataIndex: 'id',
+    fixed: 'left',
+    width: 60,
+  },
+  {
+    title: '商品名称',
+    dataIndex: 'name',
+
+    width: 160,
+  },
+  {
+    title: '商品描述',
+    dataIndex: 'desc',
+    width: 150,
+  },
+  {
+    title: '购买链接',
+    dataIndex: 'link',
+    slots: { customRender: 'link' },
+    width: 150,
+  },
+  {
+    title: '商品分类',
+    dataIndex: 'productType',
+    slots: { customRender: 'productType' },
+    sorter: true,
+    width: 120,
+  },
+  {
+    title: '直播间名称',
+    dataIndex: 'steamRoom.name',
+    sorter: true,
+    width: 120,
+  },
+  {
+    title: '零售价格',
+    dataIndex: 'unit',
+    sorter: true,
+    width: 80,
+  },
+  {
+    title: '销售量',
+    dataIndex: 'amount',
+    sorter: true,
+    width: 80,
+  },
+  {
+    title: '市场价',
+    dataIndex: 'marketingUnit',
+    sorter: true,
+    width: 80,
+  },
+  {
+    title: '下单时间',
+    dataIndex: 'createTime',
+    sorter: true,
+    width: 140,
+  },
+  {
+    title: '上架状态',
+    dataIndex: 'isLaunched',
+    width: 180,
+    customRender: ({ record }) => {
+      if (!Reflect.has(record, 'pendingStatus')) {
+        record.pendingStatus = false;
+      }
+      return h(Switch, {
+        checked: record.isLaunched,
+        checkedChildren: '上架',
+        unCheckedChildren: '下架',
+        loading: false,
+        onChange(checked: boolean) {
+          record.pendingStatus = true;
+          const newStatus = checked ? '1' : '0';
+          const { createMessage } = useMessage();
+          createMessage.info(`暂未接入` + newStatus);
+        },
+      });
+    },
+  },
+  {
+    title: '操作',
+    dataIndex: '',
+    slots: { customRender: 'action' },
+    fixed: 'right',
+    width: 140,
+  },
+];

+ 205 - 2
src/views/dashboard/product/list.vue

@@ -1,5 +1,208 @@
 <template>
-  <div> 商品列表 </div>
+  <div class="p-4">
+    <BasicTable @register="registerTable">
+      <template #toolbar> </template>
+      <template #link="{ record }">
+        <a :href="record.link" target="_blank">{{ record.link }}</a>
+      </template>
+      <template #orderType="{ record }"> {{ renderProductTypeLabel(record.orderType) }} </template>
+
+      <template #action="{ record }">
+        <TableAction
+          :actions="[
+            {
+              icon: 'mdi:information-outline',
+              label: '编辑',
+              onClick: handleEdit.bind(null, record),
+            },
+            {
+              icon: 'ant-design:delete-outlined',
+              color: 'error',
+              label: '删除',
+              popConfirm: {
+                title: '是否确认删除',
+                confirm: () => {
+                  createMessage.info(`暂未接入`);
+                },
+              },
+            },
+          ]"
+        />
+      </template>
+    </BasicTable>
+    <ProductDrawer @register="registerDrawer" />
+  </div>
 </template>
+<script lang="ts">
+  import { defineComponent, h } from 'vue';
+  import { BasicTable, useTable, BasicColumn, FormProps, TableAction } from '/@/components/Table';
+  import { Switch } from 'ant-design-vue';
+  import { useMessage } from '/@/hooks/web/useMessage';
+  import { uploadApi } from '/@/api/sys/upload';
+  // import { Switch } from 'ant-design-vue';
+  // import { h } from 'vue';
+  import { ListApi } from '/@/api/product/list';
+  import { useI18n } from '/@/hooks/web/useI18n';
+  // import { useCopyToClipboard } from '/@/hooks/web/useCopyToClipboard';
+  import { useGo } from '/@/hooks/web/usePage';
+  import { useDrawer } from '/@/components/Drawer';
+  import ProductDrawer from './productDrawer.vue';
+
+  export default defineComponent({
+    components: { BasicTable, TableAction, ProductDrawer },
+    setup() {
+      const { createMessage } = useMessage();
+      const go = useGo();
+      const { t } = useI18n();
+      const [registerDrawer, { openDrawer }] = useDrawer();
+      const columns: BasicColumn[] = [
+        {
+          title: 'ID',
+          dataIndex: 'id',
+          fixed: 'left',
+          width: 60,
+        },
+        {
+          title: '商品名称',
+          dataIndex: 'name',
+
+          width: 160,
+        },
+        {
+          title: '商品描述',
+          dataIndex: 'desc',
+          width: 150,
+        },
+        {
+          title: '购买链接',
+          dataIndex: 'link',
+          slots: { customRender: 'link' },
+          width: 150,
+        },
+        {
+          title: '商品分类',
+          dataIndex: 'productType',
+          slots: { customRender: 'productType' },
+          sorter: true,
+          width: 120,
+        },
+        {
+          title: '直播间名称',
+          dataIndex: 'steamRoom.name',
+          sorter: true,
+          width: 120,
+        },
+        {
+          title: '零售价格',
+          dataIndex: 'unit',
+          sorter: true,
+          width: 80,
+        },
+        {
+          title: '销售量',
+          dataIndex: 'amount',
+          sorter: true,
+          width: 80,
+        },
+        {
+          title: '市场价',
+          dataIndex: 'marketingUnit',
+          sorter: true,
+          width: 80,
+        },
+        {
+          title: '下单时间',
+          dataIndex: 'createTime',
+          sorter: true,
+          width: 140,
+        },
+        {
+          title: '上架状态',
+          dataIndex: 'isLaunched',
+          width: 180,
+          customRender: ({ record }) => {
+            if (!Reflect.has(record, 'pendingStatus')) {
+              record.pendingStatus = false;
+            }
+            return h(Switch, {
+              checked: record.isLaunched,
+              checkedChildren: '上架',
+              unCheckedChildren: '下架',
+              loading: false,
+              onChange(checked: boolean) {
+                record.pendingStatus = true;
+                const newStatus = checked ? '1' : '0';
+                const { createMessage } = useMessage();
+                createMessage.info(`暂未接入` + newStatus);
+              },
+            });
+          },
+        },
+        {
+          title: '操作',
+          dataIndex: '',
+          slots: { customRender: 'action' },
+          fixed: 'right',
+          width: 140,
+        },
+      ];
+
+      const searchForm: Partial<FormProps> = {
+        labelWidth: 100,
+        schemas: [
+          {
+            field: 'orderNo',
+            label: '商品名称',
+            component: 'Input',
+            colProps: {
+              xl: 5,
+              xxl: 5,
+            },
+          },
+        ],
+      };
+
+      const [registerTable] = useTable({
+        title: '商品列表',
+        api: ListApi,
+        columns: columns,
+        useSearchForm: true,
+        formConfig: searchForm,
+        showTableSetting: true,
+        tableSetting: { fullScreen: true },
+        showIndexColumn: false,
+        rowKey: 'id',
+        pagination: { pageSize: 20 },
+        bordered: true,
+      });
+
+      function renderProductTypeLabel(type: number): string {
+        switch (type) {
+          case 0:
+            return '立即购买';
+          case 1:
+            return '延后购买';
+          default:
+            return '立即购买';
+        }
+      }
+      function handleEdit(record: Recordable) {
+        openDrawer(true, {
+          record,
+          isUpdate: true,
+        });
+      }
 
-<script lang="ts" setup></script>
+      return {
+        registerTable,
+        createMessage,
+        t,
+        go,
+        renderProductTypeLabel,
+        registerDrawer,
+        handleEdit,
+        uploadApi: uploadApi as any,
+      };
+    },
+  });
+</script>

+ 70 - 0
src/views/dashboard/product/productDrawer.vue

@@ -0,0 +1,70 @@
+<template>
+  <BasicDrawer
+    v-bind="$attrs"
+    @register="registerDrawer"
+    showFooter
+    :title="getTitle"
+    width="50%"
+    @ok="handleSubmit"
+  >
+    <BasicForm @register="registerForm" />
+  </BasicDrawer>
+</template>
+<script lang="ts">
+  import { defineComponent, ref, computed, unref } from 'vue';
+  import { BasicForm, useForm } from '/@/components/Form/index';
+  import { formSchema } from './drawer.data';
+  import { BasicDrawer, useDrawerInner } from '/@/components/Drawer';
+
+  import { getMenuList } from '/@/api/system/system';
+
+  export default defineComponent({
+    name: 'ProductDrawer',
+    components: { BasicDrawer, BasicForm },
+    emits: ['success', 'register'],
+    setup(_, { emit }) {
+      const isUpdate = ref(true);
+
+      const [registerForm, { resetFields, setFieldsValue, updateSchema, validate }] = useForm({
+        labelWidth: 100,
+        schemas: formSchema,
+        showActionButtonGroup: false,
+        baseColProps: { lg: 12, md: 24 },
+      });
+
+      const [registerDrawer, { setDrawerProps, closeDrawer }] = useDrawerInner(async (data) => {
+        resetFields();
+        setDrawerProps({ confirmLoading: false });
+        isUpdate.value = !!data?.isUpdate;
+
+        if (unref(isUpdate)) {
+          setFieldsValue({
+            ...data.record,
+          });
+        }
+        const treeData = await getMenuList();
+        updateSchema({
+          field: 'parentMenu',
+          componentProps: { treeData },
+        });
+      });
+
+      const getTitle = computed(() => (!unref(isUpdate) ? '新增商品' : '编辑商品'));
+
+      async function handleSubmit() {
+        try {
+          const values = await validate();
+          setDrawerProps({ confirmLoading: true });
+          // TODO custom api
+          console.log(values);
+          closeDrawer();
+          emit('success');
+        } finally {
+          setDrawerProps({ confirmLoading: false });
+        }
+      }
+
+      return { registerDrawer, registerForm, getTitle, handleSubmit };
+    },
+  });
+</script>