Browse Source

feat(sku-manage): 新增产品品牌管理功能

- 添加产品品牌管理相关的 API 接口
- 实现产品品牌管理的表格组件和编辑及查询功能
- element-plus和sass版本升级
WanGxC 6 months ago
parent
commit
5e0762c569

+ 40 - 10
package-lock.json

@@ -28,7 +28,7 @@
 				"echarts": "^5.4.1",
 				"echarts": "^5.4.1",
 				"echarts-gl": "^2.0.9",
 				"echarts-gl": "^2.0.9",
 				"echarts-wordcloud": "^2.1.0",
 				"echarts-wordcloud": "^2.1.0",
-				"element-plus": "^2.8.1",
+				"element-plus": "^2.8.7",
 				"element-tree-line": "^0.2.1",
 				"element-tree-line": "^0.2.1",
 				"font-awesome": "^4.7.0",
 				"font-awesome": "^4.7.0",
 				"js-cookie": "^3.0.1",
 				"js-cookie": "^3.0.1",
@@ -70,7 +70,7 @@
 				"eslint": "^8.54.0",
 				"eslint": "^8.54.0",
 				"eslint-plugin-vue": "^9.8.0",
 				"eslint-plugin-vue": "^9.8.0",
 				"prettier": "^2.8.1",
 				"prettier": "^2.8.1",
-				"sass": "1.56.2",
+				"sass": "^1.79.0",
 				"typescript": "^5.6.2",
 				"typescript": "^5.6.2",
 				"unplugin-auto-import": "^0.18.3",
 				"unplugin-auto-import": "^0.18.3",
 				"unplugin-vue-components": "^0.27.4",
 				"unplugin-vue-components": "^0.27.4",
@@ -5409,9 +5409,9 @@
 			"license": "ISC"
 			"license": "ISC"
 		},
 		},
 		"node_modules/element-plus": {
 		"node_modules/element-plus": {
-			"version": "2.8.6",
-			"resolved": "https://registry.npmmirror.com/element-plus/-/element-plus-2.8.6.tgz",
-			"integrity": "sha512-fk5jB8V3efM02/4roZ5SWOLArgaYXbxEydZLlXSr+KPAwjNyHBlk2+HO5em8YKo5+RLBoHnn6BaThj6IE4nXoQ==",
+			"version": "2.8.7",
+			"resolved": "https://registry.npmmirror.com/element-plus/-/element-plus-2.8.7.tgz",
+			"integrity": "sha512-oGQyFRufFOgjd872tZc+T4xQAYLlX4hj6d3ixeY13L4fFNUuc1N49JHAqJGPda0tdx3qCnjceZoh1kqqj2+tXQ==",
 			"license": "MIT",
 			"license": "MIT",
 			"dependencies": {
 			"dependencies": {
 				"@ctrl/tinycolor": "^3.4.1",
 				"@ctrl/tinycolor": "^3.4.1",
@@ -8500,13 +8500,13 @@
 			"license": "MIT"
 			"license": "MIT"
 		},
 		},
 		"node_modules/sass": {
 		"node_modules/sass": {
-			"version": "1.56.2",
-			"resolved": "https://registry.npmmirror.com/sass/-/sass-1.56.2.tgz",
-			"integrity": "sha512-ciEJhnyCRwzlBCB+h5cCPM6ie/6f8HrhZMQOf5vlU60Y1bI1rx5Zb0vlDZvaycHsg/MqFfF1Eq2eokAa32iw8w==",
+			"version": "1.79.0",
+			"resolved": "https://registry.npmmirror.com/sass/-/sass-1.79.0.tgz",
+			"integrity": "sha512-9Q1xXsm9XT54yYkmQAoH3vCMEIavwWWQGJ3cZ0WJAgecR4edDDTdtiPyEeFDNWO/hLCy3qZKvwjK4ulPR5Yzow==",
 			"devOptional": true,
 			"devOptional": true,
 			"license": "MIT",
 			"license": "MIT",
 			"dependencies": {
 			"dependencies": {
-				"chokidar": ">=3.0.0 <4.0.0",
+				"chokidar": "^4.0.0",
 				"immutable": "^4.0.0",
 				"immutable": "^4.0.0",
 				"source-map-js": ">=0.6.2 <2.0.0"
 				"source-map-js": ">=0.6.2 <2.0.0"
 			},
 			},
@@ -8514,7 +8514,37 @@
 				"sass": "sass.js"
 				"sass": "sass.js"
 			},
 			},
 			"engines": {
 			"engines": {
-				"node": ">=12.0.0"
+				"node": ">=14.0.0"
+			}
+		},
+		"node_modules/sass/node_modules/chokidar": {
+			"version": "4.0.1",
+			"resolved": "https://registry.npmmirror.com/chokidar/-/chokidar-4.0.1.tgz",
+			"integrity": "sha512-n8enUVCED/KVRQlab1hr3MVpcVMvxtZjmEa956u+4YijlmQED223XMSYj2tLuKvr4jcCTzNNMpQDUer72MMmzA==",
+			"devOptional": true,
+			"license": "MIT",
+			"dependencies": {
+				"readdirp": "^4.0.1"
+			},
+			"engines": {
+				"node": ">= 14.16.0"
+			},
+			"funding": {
+				"url": "https://paulmillr.com/funding/"
+			}
+		},
+		"node_modules/sass/node_modules/readdirp": {
+			"version": "4.0.2",
+			"resolved": "https://registry.npmmirror.com/readdirp/-/readdirp-4.0.2.tgz",
+			"integrity": "sha512-yDMz9g+VaZkqBYS/ozoBJwaBhTbZo3UNYQHNRw1D3UFQB8oHB4uS/tAODO+ZLjGWmUbKnIlOWO+aaIiAxrUWHA==",
+			"devOptional": true,
+			"license": "MIT",
+			"engines": {
+				"node": ">= 14.16.0"
+			},
+			"funding": {
+				"type": "individual",
+				"url": "https://paulmillr.com/funding/"
 			}
 			}
 		},
 		},
 		"node_modules/sax": {
 		"node_modules/sax": {

+ 2 - 2
package.json

@@ -29,7 +29,7 @@
 		"echarts": "^5.4.1",
 		"echarts": "^5.4.1",
 		"echarts-gl": "^2.0.9",
 		"echarts-gl": "^2.0.9",
 		"echarts-wordcloud": "^2.1.0",
 		"echarts-wordcloud": "^2.1.0",
-		"element-plus": "^2.8.1",
+		"element-plus": "^2.8.7",
 		"element-tree-line": "^0.2.1",
 		"element-tree-line": "^0.2.1",
 		"font-awesome": "^4.7.0",
 		"font-awesome": "^4.7.0",
 		"js-cookie": "^3.0.1",
 		"js-cookie": "^3.0.1",
@@ -71,7 +71,7 @@
 		"eslint": "^8.54.0",
 		"eslint": "^8.54.0",
 		"eslint-plugin-vue": "^9.8.0",
 		"eslint-plugin-vue": "^9.8.0",
 		"prettier": "^2.8.1",
 		"prettier": "^2.8.1",
-		"sass": "1.56.2",
+		"sass": "^1.79.0",
 		"typescript": "^5.6.2",
 		"typescript": "^5.6.2",
 		"unplugin-auto-import": "^0.18.3",
 		"unplugin-auto-import": "^0.18.3",
 		"unplugin-vue-components": "^0.27.4",
 		"unplugin-vue-components": "^0.27.4",

+ 10 - 39
src/views/sku-manage/Columns.ts

@@ -1,7 +1,7 @@
 export const AttributeColumns = [
 export const AttributeColumns = [
   { type: 'seq', title: 'No.', width: 60, align: 'center', fixed: 'left' },
   { type: 'seq', title: 'No.', width: 60, align: 'center', fixed: 'left' },
   {
   {
-    field: 'name', title: '属性名称', minWidth: 'auto', align: 'center',fixed: 'left',
+    field: 'name', title: '属性名称', minWidth: 'auto', align: 'center', fixed: 'left',
     slots: { default: 'name' }
     slots: { default: 'name' }
   },
   },
   {
   {
@@ -22,47 +22,18 @@ export const AttributeColumns = [
   }
   }
 ];
 ];
 
 
-export const OnlineMerchandiseColumns = [
-  { type: 'seq', title: 'No.', width: 60, align: 'center', fixed: 'left', },
-  { field: 'asin', title: 'ASIN', minWidth: 'auto', align: 'center', slots: { default: 'asin' }, fixed: 'left', },
-  { field: 'sku', title: 'SKU', minWidth: 'auto', align: 'center', slots: { default: 'sku' } },
-  { field: 'platform_number', title: '平台编号', minWidth: 'auto', align: 'center',
-    slots: { default: 'platform_number' }
-  },
-  {
-    field: 'shop_name', title: '店 铺', minWidth: 'auto', align: 'center',
-    slots: { default: 'shop_name' }
-  },
-  {
-    field: 'country_code', title: '国 家', width: 'auto', align: 'center',
-    slots: { default: 'country_code' }
-  },
-  {
-    field: 'region', title: '站 点', width: 'auto', align: 'center',
-    slots: { default: 'region' }
-  },
-  {
-    field: 'quantity', title: '可售数量', width: 'auto', align: 'center',
-    slots: { default: 'quantity' }
-  },
-  {
-    field: 'fulfillment_channel', title: '配送方式', width: 'auto', align: 'center',
-    slots: { default: 'fulfillment_channel' }
-  },
-  {
-    field: 'raw_launch_datetime', title: '上架时间', width: 'auto', align: 'center',
-    slots: { default: 'raw_launch_datetime' }
-  },
-  {
-    field: 'price', title: '价 格', width: 'auto', align: 'center',
-    slots: { default: 'price' }
+export const ProductBrandColumns = [
+  { type: 'seq', title: 'No.', width: 60, align: 'center', fixed: 'left' },
+  { field: 'brand_name', title: '品牌名称',  align: 'center', fixed: 'left',
+    slots: { default: 'brand_name' }
   },
   },
   {
   {
-    field: 'status', title: '状 态', width: 80, align: 'center',
-    slots: { default: 'status' }
+    field: 'update_datetime', title: '更新时间', align: 'center',
+    slots: { default: 'update_datetime' }
   },
   },
+  { field: 'create_datetime', title: '创建时间',  align: 'center', slots: { default: 'create_datetime' } },
   {
   {
-    field: 'create_datetime', title: '创建时间', minWidth: 'auto', align: 'center',
-    slots: { default: 'create_datetime' }
+    field: 'operate', title: '操 作', align: 'center', fixed: 'right',
+    slots: { default: 'operate' }
   }
   }
 ];
 ];

+ 1 - 1
src/views/sku-manage/product-attribute/component/EditDrawer.vue

@@ -70,7 +70,7 @@ function closeDrawer() {
                v-model="editOpen"
                v-model="editOpen"
                :close-on-click-modal="false"
                :close-on-click-modal="false"
                :close-on-press-escape="false"
                :close-on-press-escape="false"
-               :title="`竞品监控- 编辑 `"
+               :title="`产品属性- 编辑 `"
                size="25%">
                size="25%">
       <el-form
       <el-form
           ref="ruleFormRef"
           ref="ruleFormRef"

+ 29 - 0
src/views/sku-manage/product-brand/api.ts

@@ -0,0 +1,29 @@
+import { request } from '/@/utils/service';
+
+
+const apiPrefix = '/api/cms/sku_brand/';
+
+export function getTableData(query: any) {
+  return request({
+    url: apiPrefix,
+    method: 'GET',
+    params: query
+  });
+}
+
+export function deleteRow(query: any) {
+  return request({
+    url: apiPrefix + query.id + '/',
+    method: 'DELETE',
+    params: query
+  });
+}
+
+export function updateRow(query: any) {
+  return request({
+    url: apiPrefix + query.id + '/',
+    method: 'PUT',
+    data: query
+  });
+}
+

+ 189 - 0
src/views/sku-manage/product-brand/component/DataTable.vue

@@ -0,0 +1,189 @@
+<script lang="ts" setup>
+/**
+ * @Name: Table.vue
+ * @Description: 产品属性表格
+ * @Author: Cheney
+ */
+
+import { ElMessage } from 'element-plus';
+import { Download, InfoFilled, Refresh, Plus } from '@element-plus/icons-vue';
+import { usePagination } from '/@/utils/usePagination';
+import { useTableData } from '/@/utils/useTableData';
+import { useResponse } from '/@/utils/useResponse';
+import { ProductBrandColumns } from '/@/views/sku-manage/Columns';
+import PermissionButton from '/@/components/PermissionButton/index.vue';
+import DataTableSlot from './DataTableSlot.vue';
+import EditDrawer from './EditDrawer.vue';
+import NoticeDialog from '/src/views/product-manage/product-list/component/NoticeDialog.vue';
+import * as api from '../api';
+
+
+interface Parameter {
+  name: string,
+}
+
+const queryParameter: Parameter | undefined = inject('query-parameter');
+const { tableOptions, handlePageChange } = usePagination(fetchList);
+
+const gridRef = ref();
+const gridOptions: any = reactive({
+  id: 'product-attribute-table',
+  keepSource: true,
+  size: 'small',
+  border: false,
+  round: true,
+  stripe: true,
+  currentRowHighLight: true,
+  height: '100%',
+  customConfig: {
+    storage: true,
+  },
+  toolbarConfig: {
+    size: 'large',
+    custom: true,
+    slots: {
+      tools: 'toolbar_tools',
+      buttons: 'toolbar_buttons'
+    }
+  },
+  rowConfig: {
+    isHover: true
+  },
+  columnConfig: {
+    resizable: true
+  },
+  pagerConfig: {
+    total: tableOptions.value.total,
+    page: tableOptions.value.page,
+    limit: tableOptions.value.limit
+  },
+  loading: false,
+  loadingConfig: {
+    icon: 'vxe-icon-indicator roll',
+    text: '正在拼命加载中...'
+  },
+  columns: '',
+  data: ''
+});
+
+const editOpen = ref(false);
+const rowData = ref({});
+
+const dialogVisible = ref(false);
+
+onMounted(() => {
+  fetchList();
+});
+
+async function fetchList() {
+  gridOptions.data = [];
+  gridOptions.columns = [];
+
+  const query = {
+    brand_name: queryParameter?.name,
+  };
+
+  await useTableData(api.getTableData, query, gridOptions);
+  await gridRef.value.loadColumn(ProductBrandColumns);
+  gridOptions.showHeader = Boolean(gridOptions.data?.length);
+}
+
+function handleRefresh() {
+  fetchList();
+}
+
+function handleEdit(row: any) {
+  editOpen.value = true;
+  rowData.value = row;
+}
+
+async function singleDelete(row: any) {
+  const res = await useResponse(api.deleteRow, row);
+  if (res.code === 2000) {
+    ElMessage.success({ message: '删除成功', plain: true });
+    handleRefresh();
+  }
+}
+
+async function handleDownload() {
+}
+
+const gridEvents = {
+  custom ({ type }: any) {
+    // console.log(`点击 ${type}`)
+    if (type == 'confirm') {
+      fetchList();
+    }
+  }
+}
+
+defineExpose({ fetchList });
+</script>
+
+<template>
+  <vxe-grid ref="gridRef" v-bind="gridOptions" v-on="gridEvents">
+    <template #toolbar_buttons>
+      <PermissionButton type="primary" :icon="Plus" plain round>新 增</PermissionButton>
+    </template>
+    <!-- 工具栏右侧 -->
+    <template #toolbar_tools>
+      <el-button circle class="toolbar-btn" @click="handleRefresh">
+        <el-icon>
+          <Refresh />
+        </el-icon>
+      </el-button>
+      <el-popconfirm
+          :icon="InfoFilled"
+          icon-color="#626AEF"
+          title="是否确认导出当前时间内所有数据项?"
+          width="220"
+          @confirm="handleDownload"
+      >
+        <template #reference>
+          <el-button circle class="mr-3 toolbar-btn">
+            <el-icon>
+              <Download />
+            </el-icon>
+          </el-button>
+        </template>
+        <template #actions="{ confirm, cancel }">
+          <el-button size="small" @click="cancel">No!</el-button>
+          <el-button
+              size="small"
+              type="danger"
+              @click="confirm"
+          >
+            Yes?
+          </el-button>
+        </template>
+      </el-popconfirm>
+    </template>
+    <template #pager>
+      <vxe-pager
+          v-model:currentPage="gridOptions.pagerConfig.page"
+          v-model:pageSize="gridOptions.pagerConfig.limit"
+          :total="gridOptions.pagerConfig.total"
+          class="mt-1.5"
+          @page-change="handlePageChange"
+      />
+    </template>
+    <!-- 自定义列插槽 -->
+    <template v-for="col in ProductBrandColumns" #[`${col.field}`]="{ row }">
+      <DataTableSlot :key="row.id" :field="col.field" :row="row" @edit-row="handleEdit" @handle-delete="singleDelete" />
+    </template>
+  </vxe-grid>
+  <EditDrawer v-if="editOpen" v-model="editOpen" :row-data="rowData" @refresh="handleRefresh" />
+  <NoticeDialog v-if="dialogVisible" v-model="dialogVisible" :row-data="rowData" />
+</template>
+
+<style scoped>
+.toolbar-btn {
+  width: 34px;
+  height: 34px;
+  font-size: 18px
+}
+
+:deep(.custom-el-input .el-select__wrapper) {
+  border-radius: 20px;
+}
+</style>

+ 73 - 0
src/views/sku-manage/product-brand/component/DataTableSlot.vue

@@ -0,0 +1,73 @@
+<script lang="ts" setup>
+/**
+ * @Name: DataTableSlot.vue
+ * @Description: 产品属性-单元格插槽
+ * @Author: Cheney
+ */
+
+import { Delete, InfoFilled, Operation } from '@element-plus/icons-vue';
+import PermissionButton from '/@/components/PermissionButton/index.vue';
+
+
+const props = defineProps<{
+  row: any,
+  field: any
+}>();
+const { row, field } = props;
+
+const emit = defineEmits([ 'edit-row', 'handle-delete' ]);
+
+function handleEdit() {
+  emit('edit-row', row);
+}
+
+function onConfirm() {
+  emit('handle-delete', row);
+}
+</script>
+
+<template>
+  <div class="font-medium">
+    <div v-if="field === 'operate'">
+      <div class="flex justify-center gap-2">
+        <PermissionButton circle plain type="warning" @click="handleEdit">
+          <el-icon>
+            <Operation />
+          </el-icon>
+        </PermissionButton>
+        <el-popconfirm
+            :icon="InfoFilled"
+            icon-color="#626AEF"
+            title="你确定要删除此项吗?"
+            width="220"
+            @confirm="onConfirm"
+        >
+          <template #reference>
+            <PermissionButton circle plain type="danger">
+              <el-icon>
+                <Delete />
+              </el-icon>
+            </PermissionButton>
+          </template>
+          <template #actions="{ confirm, cancel }">
+            <el-button size="small" @click="cancel">No!</el-button>
+            <el-button
+                size="small"
+                type="danger"
+                @click="confirm"
+            >
+              Yes?
+            </el-button>
+          </template>
+        </el-popconfirm>
+      </div>
+    </div>
+    <div v-else>
+      {{ row[field] }}
+    </div>
+  </div>
+</template>
+
+<style scoped>
+
+</style>

+ 107 - 0
src/views/sku-manage/product-brand/component/EditDrawer.vue

@@ -0,0 +1,107 @@
+<script lang="ts" setup>
+/**
+ * @Name: EditDrawer.vue
+ * @Description: 产品属性-行编辑
+ * @Author: Cheney
+ */
+
+import { ElMessage, FormInstance, FormRules } from 'element-plus';
+import { Close, Finished } from '@element-plus/icons-vue';
+import { useResponse } from '/@/utils/useResponse';
+import * as api from '../api';
+
+
+const btnLoading = ref(false);
+
+const editOpen = defineModel({ default: false });
+
+const editDrawer = <Ref>useTemplateRef('editDrawer');
+
+const props = defineProps({
+  rowData: Object
+});
+const { rowData } = props;
+const emit = defineEmits([ 'refresh' ]);
+
+interface RuleForm {
+  brand_name: any,
+}
+
+const ruleFormRef = ref<FormInstance>();
+const ruleForm = reactive<RuleForm>({
+  brand_name: rowData?.brand_name
+});
+
+const rules = reactive<FormRules<RuleForm>>({
+  // shop_name: [ { required: true, message: '请输入店铺', trigger: 'blur' } ],
+});
+
+const submitForm = async (formEl: FormInstance | undefined) => {
+  if (!formEl) return;
+  await formEl.validate(async (valid, fields) => {
+    if (valid) {
+      try {
+        const res = await useResponse(api.updateRow, { id: rowData?.id, ...ruleForm }, btnLoading);
+        if (res && res.code == 2000) {
+          editOpen.value = false;
+          ElMessage.success('编辑成功');
+          emit('refresh');
+        }
+      } catch (error) {
+        console.error('Error==>', error);
+      }
+    } else {
+      console.log('error submit!', fields);
+    }
+  });
+};
+
+function closeDrawer() {
+  editDrawer.value.handleClose();
+}
+
+</script>
+
+<template>
+  <div class="drawer-container">
+    <el-drawer ref="editDrawer"
+               v-model="editOpen"
+               :close-on-click-modal="false"
+               :close-on-press-escape="false"
+               :title="`产品品牌- 编辑 `"
+               size="25%">
+      <el-form
+          ref="ruleFormRef"
+          :model="ruleForm"
+          :rules="rules"
+          class="mx-2.5 mt-7"
+          label-position="top"
+          label-width="auto"
+          status-icon>
+        <el-form-item class="font-medium" label="品牌名称" prop="name">
+          <el-input v-model="ruleForm.brand_name" />
+        </el-form-item>
+        <el-form-item>
+          <el-divider />
+          <div class="flex flex-1 justify-end">
+            <el-button :icon="Close" @click="closeDrawer">取 消</el-button>
+            <el-button :icon="Finished" :loading="btnLoading" type="primary" @click="submitForm(ruleFormRef)">
+              确 定
+            </el-button>
+          </div>
+        </el-form-item>
+      </el-form>
+    </el-drawer>
+  </div>
+</template>
+
+<style scoped>
+.drawer-container :deep(.el-drawer__header) {
+  border-bottom: none;
+  font-weight: 500;
+}
+
+.drawer-container :deep(.el-drawer__title) {
+  font-size: 18px;
+}
+</style>

+ 78 - 0
src/views/sku-manage/product-brand/index.vue

@@ -0,0 +1,78 @@
+<script lang="ts" setup>
+/**
+ * @Name: index.vue
+ * @Description: 产品属性
+ * @Author: Cheney
+ */
+
+import { RefreshRight, Search } from '@element-plus/icons-vue';
+import DataTable from './component/DataTable.vue';
+import VerticalDivider from '/@/components/VerticalDivider/index.vue';
+import { useTableHeight } from '/@/utils/useTableHeight';
+
+
+const titleContainer: Ref<HTMLElement | null> = useTemplateRef('titleContainer');
+const queryContainer: Ref<HTMLElement | null> = useTemplateRef('queryContainer');
+const { tableHeight } = useTableHeight(titleContainer, queryContainer);
+
+const tableRef: Ref<any> = useTemplateRef('table');
+
+const btnLoading = ref(false);
+
+const formInline = reactive<any>({
+  name: '',
+});
+provide('query-parameter', formInline);
+
+async function handleQuery() {
+  btnLoading.value = true;
+  await tableRef.value?.fetchList();
+  btnLoading.value = false;
+}
+
+function resetParameter() {
+  for (const key in formInline) {
+    formInline[key] = '';
+  }
+}
+</script>
+
+<template>
+  <div class="p-5 flex-grow">
+    <el-card class="h-full" style="color: rgba(0, 0, 0, 0.88);">
+      <div ref="titleContainer" class="text-xl font-semibold pb-5">产品属性</div>
+      <!-- 查询条件 -->
+      <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: 5px;">
+              <el-col :span="5">
+                <div class="flex items-center">
+                  <span class="mr-2">品牌名称</span>
+                  <el-input v-model="formInline.name" clearable placeholder="请输入品牌名称" />
+                </div>
+              </el-col>
+            </el-row>
+          </div>
+        </div>
+        <VerticalDivider />
+        <div class="flex gap-1.5 ml-5">
+          <el-button :icon="Search" :loading="btnLoading" type="primary" @click="handleQuery">
+            查 询
+          </el-button>
+          <el-button :icon="RefreshRight" color="#ECECF1C9" style="width: 88px; color: #3c3c3c;" @click="resetParameter">
+            重 置
+          </el-button>
+        </div>
+      </div>
+      <el-divider ref="dividerContainer" style="margin: 20px 0 12px 0;" />
+      <div :style="{ height: tableHeight + 'px' }">
+        <DataTable ref="table" />
+      </div>
+    </el-card>
+  </div>
+</template>
+
+<style scoped>
+
+</style>

+ 6 - 1
vite.config.ts

@@ -113,7 +113,12 @@ const viteConfig = defineConfig((mode: ConfigEnv) => {
         }
         }
       }
       }
     },
     },
-    css: { preprocessorOptions: { css: { charset: false } } },
+    css: { 
+      preprocessorOptions: { 
+        css: { charset: false },
+        scss: { api: 'modern-compiler' },
+      } 
+    },
     define: {
     define: {
       __VUE_I18N_LEGACY_API__: JSON.stringify(false),
       __VUE_I18N_LEGACY_API__: JSON.stringify(false),
       __VUE_I18N_FULL_INSTALL__: JSON.stringify(false),
       __VUE_I18N_FULL_INSTALL__: JSON.stringify(false),