Преглед на файлове

feat: 国际化配置以及商品模块翻译

叶静 преди 2 дни
родител
ревизия
5f4c2a2920

+ 1 - 1
.env.development

@@ -21,7 +21,7 @@ SHEEP_USE_MOCK = false
 # 代理配置
 SHEEP_USE_PROXY = false
 SHEEP_PROXY_PREFIX = '/api'
-SHEEP_PROXY_TARGET = http://192.168.0.109:8401
+SHEEP_PROXY_TARGET = http://124.222.152.234:8501
 
 ##### API路由配置
 # API路由功能总开关

+ 6 - 43
.promptx/pouch.json

@@ -1,67 +1,30 @@
 {
-  "currentState": "memory_saved",
+  "currentState": "role_activated_with_memory",
   "stateHistory": [
     {
       "from": "initial",
       "command": "init",
-      "timestamp": "2025-08-14T02:15:22.172Z",
+      "timestamp": "2025-08-21T09:54:20.538Z",
       "args": [
         {
-          "workingDirectory": "d:\\work\\bandhu-buy\\admin",
-          "ideType": "cursor"
+          "workingDirectory": "d:\\work\\bandhu-buy\\admin"
         }
       ]
     },
     {
       "from": "initialized",
       "command": "welcome",
-      "timestamp": "2025-08-14T02:15:26.148Z",
-      "args": []
-    },
-    {
-      "from": "service_discovery",
-      "command": "init",
-      "timestamp": "2025-08-14T02:16:18.292Z",
-      "args": [
-        {
-          "workingDirectory": "d:\\work\\bandhu-buy\\admin",
-          "ideType": "cursor"
-        }
-      ]
-    },
-    {
-      "from": "initialized",
-      "command": "welcome",
-      "timestamp": "2025-08-14T02:16:22.399Z",
+      "timestamp": "2025-08-21T09:54:25.021Z",
       "args": []
     },
     {
       "from": "service_discovery",
       "command": "action",
-      "timestamp": "2025-08-14T02:17:21.876Z",
+      "timestamp": "2025-08-21T09:54:29.854Z",
       "args": [
         "vue3-expert"
       ]
-    },
-    {
-      "from": "role_activated_with_memory",
-      "command": "recall",
-      "timestamp": "2025-08-14T02:20:00.318Z",
-      "args": [
-        "vue3-expert"
-      ]
-    },
-    {
-      "from": "recalled-vue3-expert",
-      "command": "remember",
-      "timestamp": "2025-08-14T02:21:40.031Z",
-      "args": [
-        "vue3-expert",
-        "项目已引入UnoCSS原子化CSS框架,后续开发中有样式需求时,优先使用UnoCSS的原子化类名而不是自定义CSS。UnoCSS配置文件为uno.config.js,支持Preset Uno和Transformer Directives。开发时应该:1. 优先使用UnoCSS原子化类名(如 flex, items-center, justify-between, p-4, m-2等);2. 减少自定义CSS的编写;3. 保持与现有sa-前缀组件的兼容性;4. 利用UnoCSS的响应式、状态变体等高级特性。",
-        "--tags",
-        "UnoCSS 原子化CSS 样式优先级 开发规范"
-      ]
     }
   ],
-  "lastUpdated": "2025-08-14T02:21:40.043Z"
+  "lastUpdated": "2025-08-21T09:54:29.880Z"
 }

+ 11 - 11
.promptx/resource/project.registry.json

@@ -4,8 +4,8 @@
   "metadata": {
     "version": "2.0.0",
     "description": "project 级资源注册表",
-    "createdAt": "2025-08-14T02:16:18.307Z",
-    "updatedAt": "2025-08-14T02:16:18.316Z",
+    "createdAt": "2025-08-21T09:54:20.548Z",
+    "updatedAt": "2025-08-21T09:54:20.552Z",
     "resourceCount": 3
   },
   "resources": [
@@ -17,9 +17,9 @@
       "description": "执行模式,定义具体的行为模式",
       "reference": "@project://.promptx/resource/role/vue3-expert/execution/vue3-development.execution.md",
       "metadata": {
-        "createdAt": "2025-08-14T02:16:18.311Z",
-        "updatedAt": "2025-08-14T02:16:18.311Z",
-        "scannedAt": "2025-08-14T02:16:18.311Z",
+        "createdAt": "2025-08-21T09:54:20.550Z",
+        "updatedAt": "2025-08-21T09:54:20.550Z",
+        "scannedAt": "2025-08-21T09:54:20.550Z",
         "path": "role/vue3-expert/execution/vue3-development.execution.md"
       }
     },
@@ -31,9 +31,9 @@
       "description": "思维模式,指导AI的思考方式",
       "reference": "@project://.promptx/resource/role/vue3-expert/thought/vue3-thinking.thought.md",
       "metadata": {
-        "createdAt": "2025-08-14T02:16:18.313Z",
-        "updatedAt": "2025-08-14T02:16:18.313Z",
-        "scannedAt": "2025-08-14T02:16:18.313Z",
+        "createdAt": "2025-08-21T09:54:20.551Z",
+        "updatedAt": "2025-08-21T09:54:20.551Z",
+        "scannedAt": "2025-08-21T09:54:20.551Z",
         "path": "role/vue3-expert/thought/vue3-thinking.thought.md"
       }
     },
@@ -45,9 +45,9 @@
       "description": "专业角色,提供特定领域的专业能力",
       "reference": "@project://.promptx/resource/role/vue3-expert/vue3-expert.role.md",
       "metadata": {
-        "createdAt": "2025-08-14T02:16:18.315Z",
-        "updatedAt": "2025-08-14T02:16:18.315Z",
-        "scannedAt": "2025-08-14T02:16:18.314Z",
+        "createdAt": "2025-08-21T09:54:20.551Z",
+        "updatedAt": "2025-08-21T09:54:20.551Z",
+        "scannedAt": "2025-08-21T09:54:20.551Z",
         "path": "role/vue3-expert/vue3-expert.role.md"
       }
     }

+ 1 - 0
package.json

@@ -40,6 +40,7 @@
     "v-contextmenu": "^3.0.0",
     "vue": "^3.3.5",
     "vue-echarts": "^6.6.1",
+    "vue-i18n": "9",
     "vue-jsonp": "^2.0.0",
     "vue-router": "^4.2.5",
     "vuedraggable": "^4.1.0",

+ 40 - 0
pnpm-lock.yaml

@@ -86,6 +86,9 @@ importers:
       vue-echarts:
         specifier: ^6.6.1
         version: 6.7.3(@vue/runtime-core@3.5.18)(echarts@5.6.0)(vue@3.5.18)
+      vue-i18n:
+        specifier: '9'
+        version: 9.14.5(vue@3.5.18)
       vue-jsonp:
         specifier: ^2.0.0
         version: 2.1.0
@@ -482,6 +485,18 @@ packages:
       '@types/node':
         optional: true
 
+  '@intlify/core-base@9.14.5':
+    resolution: {integrity: sha512-5ah5FqZG4pOoHjkvs8mjtv+gPKYU0zCISaYNjBNNqYiaITxW8ZtVih3GS/oTOqN8d9/mDLyrjD46GBApNxmlsA==}
+    engines: {node: '>= 16'}
+
+  '@intlify/message-compiler@9.14.5':
+    resolution: {integrity: sha512-IHzgEu61/YIpQV5Pc3aRWScDcnFKWvQA9kigcINcCBXN8mbW+vk9SK+lDxA6STzKQsVJxUPg9ACC52pKKo3SVQ==}
+    engines: {node: '>= 16'}
+
+  '@intlify/shared@9.14.5':
+    resolution: {integrity: sha512-9gB+E53BYuAEMhbCAxVgG38EZrk59sxBtv3jSizNL2hEWlgjBjAw1AwpLHtNaeda12pe6W20OGEa0TwuMSRbyQ==}
+    engines: {node: '>= 16'}
+
   '@jridgewell/gen-mapping@0.3.12':
     resolution: {integrity: sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==}
 
@@ -2641,6 +2656,12 @@ packages:
   vue-flow-layout@0.2.0:
     resolution: {integrity: sha512-zKgsWWkXq0xrus7H4Mc+uFs1ESrmdTXlO0YNbR6wMdPaFvosL3fMB8N7uTV308UhGy9UvTrGhIY7mVz9eN+L0Q==}
 
+  vue-i18n@9.14.5:
+    resolution: {integrity: sha512-0jQ9Em3ymWngyiIkj0+c/k7WgaPO+TNzjKSNq9BvBQaKJECqn9cd9fL4tkDhB5G1QBskGl9YxxbDAhgbFtpe2g==}
+    engines: {node: '>= 16'}
+    peerDependencies:
+      vue: ^3.0.0
+
   vue-jsonp@2.1.0:
     resolution: {integrity: sha512-kezmjaAcMWdieO3tWxniC+82DitYUYjR1e2GsWIKHCTf+zhWUt2nPhN3dnmnAVhDQ+po3BspM7sKjiQaIhijUg==}
 
@@ -3070,6 +3091,18 @@ snapshots:
     optionalDependencies:
       '@types/node': 24.2.0
 
+  '@intlify/core-base@9.14.5':
+    dependencies:
+      '@intlify/message-compiler': 9.14.5
+      '@intlify/shared': 9.14.5
+
+  '@intlify/message-compiler@9.14.5':
+    dependencies:
+      '@intlify/shared': 9.14.5
+      source-map-js: 1.2.1
+
+  '@intlify/shared@9.14.5': {}
+
   '@jridgewell/gen-mapping@0.3.12':
     dependencies:
       '@jridgewell/sourcemap-codec': 1.5.4
@@ -5576,6 +5609,13 @@ snapshots:
 
   vue-flow-layout@0.2.0: {}
 
+  vue-i18n@9.14.5(vue@3.5.18):
+    dependencies:
+      '@intlify/core-base': 9.14.5
+      '@intlify/shared': 9.14.5
+      '@vue/devtools-api': 6.6.4
+      vue: 3.5.18
+
   vue-jsonp@2.1.0: {}
 
   vue-router@4.5.1(vue@3.5.18):

+ 9 - 4
src/app/admin/views/auth/admin/index.vue

@@ -2,10 +2,12 @@
   <el-container class="admin-view panel-block">
     <el-header class="sa-header">
       <div class="sa-title sa-flex sa-row-between">
-        <div class="label sa-flex">人员管理</div>
+        <div class="label sa-flex">{{ t('modules.auth.userManagement') }}</div>
         <div>
           <el-button class="sa-button-refresh" icon="RefreshRight" @click="getData()"></el-button>
-          <el-button icon="Plus" type="primary" @click="addRow">新增人员</el-button>
+          <el-button icon="Plus" type="primary" @click="addRow">{{
+            t('modules.auth.addAdmin')
+          }}</el-button>
         </div>
       </div>
     </el-header>
@@ -24,12 +26,12 @@
           </template>
           <el-table-column prop="id" label="ID" min-width="100" sortable="custom">
           </el-table-column>
-          <el-table-column prop="name" label="用户名" min-width="150">
+          <el-table-column prop="name" :label="t('modules.auth.username')" min-width="150">
             <template #default="scope">
               <span class="sa-table-line-1">{{ scope.row.name || '-' }}</span>
             </template>
           </el-table-column>
-          <el-table-column label="状态" min-width="120" align="center">
+          <el-table-column :label="t('form.status')" min-width="120" align="center">
             <template #default="scope">
               <!-- ID为1的用户状态不可修改 -->
               <el-tag
@@ -111,7 +113,10 @@
   import { ElMessage } from 'element-plus';
   import { ArrowDown } from '@element-plus/icons-vue';
   import { useModal, usePagination } from '@/sheep/hooks';
+  import { useI18n } from 'vue-i18n';
   import admin from '@/app/admin/api';
+
+  const { t } = useI18n();
   import AdminEdit from './edit.vue';
   import PasswordEdit from './password.vue';
 

+ 69 - 55
src/app/shop/admin/goods/category/edit.vue

@@ -1,70 +1,84 @@
 <template>
   <el-container>
     <el-main>
-      <el-form :model="form.model" :rules="form.rules" ref="formRef" label-width="100px">
-        <el-form-item label="分类名称" prop="name">
-          <el-input v-model="form.model.name" placeholder="请填写商品分类名称"></el-input>
+      <el-form :model="form.model" :rules="form.rules" ref="formRef" :label-width="formLabelWidth">
+        <el-form-item :label="t('modules.goods.categoryName')" prop="name">
+          <el-input v-model="form.model.name" :placeholder="t('form.inputCategoryName')"></el-input>
         </el-form-item>
-        <el-form-item label="排序" prop="sort">
-          <el-input v-model="form.model.sort" placeholder="请填写排序"></el-input>
+        <el-form-item :label="t('form.sort')" prop="sort">
+          <el-input v-model="form.model.sort" :placeholder="t('form.inputSort')"></el-input>
         </el-form-item>
       </el-form>
     </el-main>
     <el-footer class="sa-footer--submit">
-      <el-button v-if="modal.params.type == 'add'" type="primary" @click="confirm">保存</el-button>
-      <el-button v-if="modal.params.type == 'edit'" type="primary" @click="confirm">更新</el-button>
+      <el-button v-if="modal.params.type == 'add'" type="primary" @click="confirm">{{
+        t('common.save')
+      }}</el-button>
+      <el-button v-if="modal.params.type == 'edit'" type="primary" @click="confirm">{{
+        t('common.update')
+      }}</el-button>
     </el-footer>
   </el-container>
 </template>
 <script setup>
-  import { cloneDeep } from 'lodash';
-  import { onMounted, reactive, ref, unref } from 'vue';
-  import { api } from '../goods.service';
-  const emit = defineEmits(['modalCallBack']);
-  const props = defineProps({
-    modal: {
-      type: Object,
-    },
-  });
-  // 添加 编辑 form
-  let formRef = ref(null);
-  const form = reactive({
-    model: {
-      name: '',
-      sort: '0',
-    },
-    rules: {
-      name: [{ required: true, message: '请填写商品分类名称', trigger: 'blur' }],
-    },
-  });
-  const loading = ref(false);
-  // 获取详情
-  async function getDetail(id) {
-    loading.value = true;
-    const { code, data } = await api.category.detail(id);
-    code === '200' && (form.model = data);
-    loading.value = false;
-  }
-  // 表单关闭时提交
-  async function confirm() {
-    unref(formRef).validate(async (valid) => {
-      if (!valid) return;
-      let submitForm = cloneDeep(form.model);
-      const { code } =
-        props.modal.params.type == 'add'
-          ? await api.category.add(submitForm)
-          : await api.category.edit(submitForm);
-      if (code == '200') {
-        emit('modalCallBack', { event: 'confirm' });
-      }
-    });
-  }
-  async function init() {
-    if (props.modal.params.id) {
-      await getDetail(props.modal.params.id);
+import { cloneDeep } from 'lodash';
+import { onMounted, reactive, ref, unref } from 'vue';
+import { useI18n } from 'vue-i18n';
+import { useFormConfig } from '@/hooks/useFormConfig';
+import { api } from '../goods.service';
+
+const { t } = useI18n();
+
+// 使用表单配置hooks
+const { formLabelWidth } = useFormConfig({ enWidth: '140px' });
+
+const emit = defineEmits(['modalCallBack']);
+const props = defineProps({
+  modal: {
+    type: Object,
+  },
+});
+// 添加 编辑 form
+let formRef = ref(null);
+const form = reactive({
+  model: {
+    name: '',
+    sort: '0',
+  },
+  get rules() {
+    return {
+      name: [{ required: true, message: t('form.categoryNameRequired'), trigger: 'blur' }],
+    };
+  },
+});
+const loading = ref(false);
+// 获取详情
+async function getDetail(id) {
+  loading.value = true;
+  const { code, data } = await api.category.detail(id);
+  code === '200' && (form.model = data);
+  loading.value = false;
+}
+// 表单关闭时提交
+async function confirm() {
+  unref(formRef).validate(async (valid) => {
+    if (!valid) return;
+    let submitForm = cloneDeep(form.model);
+    const { code } =
+      props.modal.params.type == 'add'
+        ? await api.category.add(submitForm)
+        : await api.category.edit(submitForm);
+    if (code == '200') {
+      emit('modalCallBack', { event: 'confirm' });
     }
-  }
-  onMounted(() => {
-    init();
   });
+}
+async function init() {
+  if (props.modal.params.id) {
+    await getDetail(props.modal.params.id);
+  }
+}
+onMounted(() => {
+  init();
+});
 </script>

+ 150 - 158
src/app/shop/admin/goods/category/index.vue

@@ -3,73 +3,60 @@
     <el-header class="sa-header">
       <!-- 简化搜索组件 -->
       <div class="search-container">
-        <sa-search-simple
-          :searchFields="searchFields"
-          :defaultValues="defaultSearchValues"
-          @search="(val) => getData(1, val)"
-          @reset="getData(1)"
-        >
+        <sa-search-simple :searchFields="searchFields" :defaultValues="defaultSearchValues"
+          @search="(val) => getData(1, val)" @reset="getData(1)">
         </sa-search-simple>
       </div>
       <div class="sa-title sa-flex sa-row-between">
-        <div class="label sa-flex">商品分类</div>
+        <div class="label sa-flex">{{ t('modules.goods.goodsCategory') }}</div>
         <div>
           <el-button class="sa-button-refresh" icon="RefreshRight" @click="getData()"></el-button>
-          <el-button icon="Plus" type="primary" @click="addRow">新建</el-button>
+          <el-button icon="Plus" type="primary" @click="addRow">{{ t('common.create') }}</el-button>
         </div>
       </div>
     </el-header>
     <el-main class="sa-p-0">
       <div class="sa-table-wrap panel-block panel-block--bottom" v-loading="loading">
-        <el-table
-          height="100%"
-          class="sa-table"
-          :data="table.data"
-          @selection-change="changeSelection"
-          @sort-change="fieldFilter"
-          row-key="id"
-          stripe
-        >
+        <el-table height="100%" class="sa-table" :data="table.data" @selection-change="changeSelection"
+          @sort-change="fieldFilter" row-key="id" stripe>
           <template #empty>
             <sa-empty />
           </template>
           <el-table-column type="selection" width="48" align="center"></el-table-column>
           <el-table-column prop="id" label="ID" min-width="100" sortable="custom">
           </el-table-column>
-          <el-table-column label="分类名称" min-width="120">
+          <el-table-column :label="t('modules.goods.categoryName')" min-width="120">
             <template #default="scope">
               <span class="sa-table-line-1">
                 {{ scope.row.name || '-' }}
               </span>
             </template>
           </el-table-column>
-          <el-table-column label="排序" min-width="80" align="center">
+          <el-table-column :label="t('form.sort')" min-width="80" align="center">
             <template #default="scope">
               {{ scope.row.sort || 0 }}
             </template>
           </el-table-column>
-          <el-table-column label="创建时间" min-width="160">
+          <el-table-column :label="t('form.createTime')" min-width="160">
             <template #default="scope">
               {{ scope.row.createTime || '-' }}
             </template>
           </el-table-column>
-          <el-table-column label="更新时间">
+          <el-table-column :label="t('form.updateTime')">
             <template #default="scope">
               {{ scope.row.updateTime || '-' }}
             </template>
           </el-table-column>
-          <el-table-column fixed="right" label="操作">
+          <el-table-column fixed="right" :label="t('common.actions')">
             <template #default="scope">
-              <el-button class="is-link" type="primary" @click="editRow(scope.row)">编辑</el-button>
-              <el-popconfirm
-                width="fit-content"
-                confirm-button-text="确认"
-                cancel-button-text="取消"
-                title="确认删除这条记录?"
-                @confirm="deleteApi(scope.row.id)"
-              >
+              <el-button class="is-link" type="primary" @click="editRow(scope.row)">{{
+                t('common.edit')
+              }}</el-button>
+              <el-popconfirm width="fit-content" :confirm-button-text="t('common.confirm')"
+                :cancel-button-text="t('common.cancel')" :title="t('message.confirmDelete')"
+                @confirm="deleteApi(scope.row.id)">
                 <template #reference>
-                  <el-button class="is-link" type="danger"> 删除 </el-button>
+                  <el-button class="is-link" type="danger">{{ t('common.delete') }}</el-button>
                 </template>
               </el-popconfirm>
             </template>
@@ -79,11 +66,8 @@
     </el-main>
     <sa-view-bar>
       <template #left>
-        <sa-batch-handle
-          :batchHandleTools="batchHandleTools"
-          :selectedLeng="table.selected.length"
-          @batchHandle="batchHandle"
-        ></sa-batch-handle>
+        <sa-batch-handle :batchHandleTools="batchHandleTools" :selectedLeng="table.selected.length"
+          @batchHandle="batchHandle"></sa-batch-handle>
       </template>
       <template #right>
         <sa-pagination :pageData="pageData" @updateFn="getData" />
@@ -92,138 +76,146 @@
   </el-container>
 </template>
 <script setup>
-  import { onMounted, reactive, ref } from 'vue';
-  import { api } from '../goods.service';
-  import { ElMessageBox } from 'element-plus';
-  import { useModal } from '@/sheep/hooks';
-  import { usePagination } from '@/sheep/hooks';
-  import categorytagEdit from './edit.vue';
-  const { pageData } = usePagination();
+import { onMounted, reactive, ref } from 'vue';
+import { api } from '../goods.service';
+import { ElMessageBox } from 'element-plus';
+import { useModal } from '@/sheep/hooks';
+import { usePagination } from '@/sheep/hooks';
+import { useI18n } from 'vue-i18n';
+import categorytagEdit from './edit.vue';
 
-  // 搜索字段配置
-  const searchFields = reactive({
-    name: {
-      type: 'input',
-      label: '分类名称',
-      placeholder: '请输入分类名称',
-      width: 200,
+const { t } = useI18n();
+const { pageData } = usePagination();
+
+// 搜索字段配置
+const searchFields = reactive({
+  name: {
+    type: 'input',
+    get label() {
+      return t('modules.goods.categoryName');
     },
+    get placeholder() {
+      return t('form.inputCategoryName');
+    },
+    width: 200,
+  },
+});
+// 默认搜索值
+const defaultSearchValues = reactive({
+  name: '',
+});
+// 列表
+const table = reactive({
+  data: [],
+  order: '',
+  sort: '',
+  selected: [],
+});
+const loading = ref(true);
+// 获取
+async function getData(page, searchParams = {}) {
+  if (page) pageData.page = page;
+  loading.value = true;
+  const { code, data } = await api.category.list({
+    page: pageData.page,
+    size: pageData.size,
+    ...searchParams,
   });
-  // 默认搜索值
-  const defaultSearchValues = reactive({
-    name: '',
-  });
-  // 列表
-  const table = reactive({
-    data: [],
-    order: '',
-    sort: '',
-    selected: [],
-  });
-  const loading = ref(true);
-  // 获取
-  async function getData(page, searchParams = {}) {
-    if (page) pageData.page = page;
-    loading.value = true;
-    const { code, data } = await api.category.list({
-      page: pageData.page,
-      size: pageData.size,
-      ...searchParams,
-    });
-    if (code == 200) {
-      table.data = data.list;
-      pageData.page = data.pageNum;
-      pageData.size = data.pageSize;
-      pageData.total = data.total;
-    }
-    loading.value = false;
-  }
-  // table 字段排序
-  function fieldFilter({ prop, order }) {
-    table.order = order == 'ascending' ? 'asc' : 'desc';
-    table.sort = prop;
-    getData();
+  if (code == 200) {
+    table.data = data.list;
+    pageData.page = data.pageNum;
+    pageData.size = data.pageSize;
+    pageData.total = data.total;
   }
-  //table批量选择
-  function changeSelection(row) {
-    table.selected = row;
-  }
-  // 分页/批量操作
-  const batchHandleTools = [
-    // {
-    //   type: 'delete',
-    //   label: '删除',
-    //   auth: 'shop.admin.category.delete',
-    //   class: 'danger',
-    // },
-  ];
-  function addRow() {
-    useModal(
-      categorytagEdit,
-      { title: '新建分类', type: 'add' },
-      {
-        confirm: () => {
-          getData();
-        },
-      },
-    );
-  }
-  function editRow(row) {
-    useModal(
-      categorytagEdit,
-      {
-        title: '编辑分类',
-        type: 'edit',
-        id: row.id,
-      },
-      {
-        confirm: () => {
-          getData();
-        },
+  loading.value = false;
+}
+// table 字段排序
+function fieldFilter({ prop, order }) {
+  table.order = order == 'ascending' ? 'asc' : 'desc';
+  table.sort = prop;
+  getData();
+}
+//table批量选择
+function changeSelection(row) {
+  table.selected = row;
+}
+// 分页/批量操作
+const batchHandleTools = [
+  // {
+  //   type: 'delete',
+  //   label: '删除',
+  //   auth: 'shop.admin.category.delete',
+  //   class: 'danger',
+  // },
+];
+function addRow() {
+  useModal(
+    categorytagEdit,
+    { title: t('modules.goods.addCategory'), type: 'add' },
+    {
+      confirm: () => {
+        getData();
       },
-    );
-  }
-  // 删除api 单独批量可以直接调用
-  async function deleteApi(id) {
-    await api.category.delete({ id });
-    getData();
-  }
-  async function batchHandle(type) {
-    let ids = [];
-    table.selected.forEach((row) => {
-      ids.push(row.id);
-    });
-    switch (type) {
-      case 'delete':
-        ElMessageBox.confirm('此操作将删除, 是否继续?', '提示', {
-          confirmButtonText: '确定',
-          cancelButtonText: '取消',
-          type: 'warning',
-        }).then(() => {
-          deleteApi(ids.join(','));
-        });
-        break;
-      default:
-        await api.category.edit(ids.join(','), {
-          status: type,
-        });
+    },
+  );
+}
+function editRow(row) {
+  useModal(
+    categorytagEdit,
+    {
+      title: t('modules.goods.editCategory'),
+      type: 'edit',
+      id: row.id,
+    },
+    {
+      confirm: () => {
         getData();
-    }
+      },
+    },
+  );
+}
+// 删除api 单独批量可以直接调用
+async function deleteApi(id) {
+  await api.category.delete({ id });
+  getData();
+}
+async function batchHandle(type) {
+  let ids = [];
+  table.selected.forEach((row) => {
+    ids.push(row.id);
+  });
+  switch (type) {
+    case 'delete':
+      ElMessageBox.confirm('此操作将删除, 是否继续?', '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning',
+      }).then(() => {
+        deleteApi(ids.join(','));
+      });
+      break;
+    default:
+      await api.category.edit(ids.join(','), {
+        status: type,
+      });
+      getData();
   }
+}
 
-  onMounted(() => {
-    getData();
-  });
+onMounted(() => {
+  getData();
+});
 </script>
 <style lang="scss" scoped>
-  .category-view {
-    .el-header {
-      height: auto;
-    }
-    .el-main {
-      .sa-table-wrap {
-        height: 100%;
-      }
+.category-view {
+  .el-header {
+    height: auto;
+  }
+
+  .el-main {
+    .sa-table-wrap {
+      height: 100%;
     }
   }
+}
 </style>

+ 1205 - 1317
src/app/shop/admin/goods/goods/edit.vue

@@ -4,9 +4,9 @@
       <!-- 步骤进度条 -->
       <div class="step-progress">
         <el-steps :active="currentStep" align-center>
-          <el-step title="商品信息" :status="getStepStatus(0)"></el-step>
-          <el-step title="商品属性" :status="getStepStatus(1)"></el-step>
-          <el-step title="提交" :status="getStepStatus(2)"></el-step>
+          <el-step :title="t('modules.goods.goodsInfo')" :status="getStepStatus(0)"></el-step>
+          <el-step :title="t('modules.goods.goodsAttributes')" :status="getStepStatus(1)"></el-step>
+          <el-step :title="t('common.submit')" :status="getStepStatus(2)"></el-step>
         </el-steps>
       </div>
 
@@ -14,73 +14,51 @@
       <div class="step-content">
         <!-- 第一步:商品信息 -->
         <div v-show="currentStep === 0" class="step-panel">
-          <el-form ref="basicFormRef" :model="formData" :rules="basicRules" label-width="120px">
+          <el-form ref="basicFormRef" :model="formData" :rules="basicRules" :label-width="formLabelWidth">
             <el-row :gutter="40">
               <!-- 左侧表单 -->
               <el-col :span="14">
-                <el-form-item label="商品分类" prop="cateId" required>
-                  <el-select
-                    v-model="formData.cateId"
-                    placeholder="请选择商品分类"
-                    clearable
-                    style="width: 100%"
-                  >
-                    <el-option
-                      v-for="category in categoryOptions"
-                      :key="category.id"
-                      :label="category.name"
-                      :value="category.id"
-                    />
+                <el-form-item :label="t('modules.goods.goodsCategory')" prop="cateId" required>
+                  <el-select v-model="formData.cateId" :placeholder="t('form.selectCategory')" clearable
+                    style="width: 100%">
+                    <el-option v-for="category in categoryOptions" :key="category.id" :label="category.name"
+                      :value="category.id" />
                   </el-select>
                 </el-form-item>
 
-                <el-form-item label="商品名称" prop="storeName" required>
-                  <el-input
-                    v-model="formData.storeName"
-                    placeholder="请输入商品名称(限100字符)"
-                    maxlength="100"
-                    show-word-limit
-                  />
+                <el-form-item :label="t('modules.goods.goodsName')" prop="storeName" required>
+                  <el-input v-model="formData.storeName" :placeholder="t('form.inputGoodsName')" maxlength="100"
+                    show-word-limit />
                 </el-form-item>
 
-                <el-form-item label="副标题" prop="keyword">
-                  <el-input
-                    v-model="formData.keyword"
-                    placeholder="请输入副标题(限50字符)"
-                    maxlength="50"
-                    show-word-limit
-                  />
+                <el-form-item :label="t('modules.goods.subtitle')" prop="keyword">
+                  <el-input v-model="formData.keyword" :placeholder="t('form.inputSubtitle')" maxlength="50"
+                    show-word-limit />
                 </el-form-item>
 
-                <el-form-item label="商品品牌" prop="itemBrand">
-                  <el-input v-model="formData.itemBrand" placeholder="请输入商品品牌" />
+                <el-form-item :label="t('modules.goods.goodsBrand')" prop="itemBrand">
+                  <el-input v-model="formData.itemBrand" :placeholder="t('form.inputGoodsBrand')" />
                 </el-form-item>
 
-                <el-form-item label="商品介绍" prop="storeInfo">
-                  <el-input
-                    v-model="formData.storeInfo"
-                    type="textarea"
-                    :rows="4"
-                    placeholder="请输入商品介绍(限500字符)"
-                    maxlength="500"
-                    show-word-limit
-                  />
+                <el-form-item :label="t('modules.goods.goodsDescription')" prop="storeInfo">
+                  <el-input v-model="formData.storeInfo" type="textarea" :rows="4"
+                    :placeholder="t('form.inputGoodsDescription')" maxlength="500" show-word-limit />
                 </el-form-item>
 
-                <el-form-item label="运费模板" prop="tempId">
-                  <div class="mt-1px">包邮</div>
+                <el-form-item :label="t('modules.goods.shippingTemplate')" prop="tempId">
+                  <div class="mt-1px">{{ t('modules.goods.freeShipping') }}</div>
                   <!-- <el-select
                     v-model="formData.tempId"
-                    placeholder="请选择运费模板"
+                    :placeholder="t('form.selectShippingTemplate')"
                     style="width: 100%"
                   >
-                    <el-option label="包邮" :value="1" />
+                    <el-option :label="t('modules.goods.freeShipping')" :value="1" />
                   </el-select> -->
                 </el-form-item>
 
-                <el-form-item label="商品货号" prop="itemNumber">
-                  <el-input v-model="formData.itemNumber" placeholder="请输入商品货号" />
-                  <div class="form-tip">如果您不输入商品货号,系统将自动生成一个唯一的货号</div>
+                <el-form-item :label="t('modules.goods.goodsNumber')" prop="itemNumber">
+                  <el-input v-model="formData.itemNumber" :placeholder="t('form.inputGoodsNumber')" />
+                  <div class="form-tip">{{ t('modules.goods.autoGenerateNumberTip') }}</div>
                 </el-form-item>
 
                 <!-- <el-form-item label="商品售价" prop="price" required>
@@ -92,32 +70,19 @@
                     step="0.01"
                   >
                     <template #append>৳</template>
-                  </el-input>
-                </el-form-item>
+</el-input>
+</el-form-item>
 
-                <el-form-item label="市场价" prop="otPrice">
-                  <el-input
-                    v-model="formData.otPrice"
-                    placeholder="请输入市场价"
-                    type="number"
-                    min="0"
-                    step="0.01"
-                  >
-                    <template #append>৳</template>
-                  </el-input>
-                </el-form-item>
+<el-form-item label="市场价" prop="otPrice">
+  <el-input v-model="formData.otPrice" placeholder="请输入市场价" type="number" min="0" step="0.01">
+    <template #append>৳</template>
+  </el-input>
+</el-form-item>
 
-                <el-form-item label="商品库存" prop="stock" required>
-                  <el-input
-                    v-model="formData.stock"
-                    placeholder="请输入商品库存"
-                    type="number"
-                    min="0"
-                  />
-                  <div class="form-tip"
-                    >该设置只对单品有效,当商品存在多规格货品时为不可编辑状态,库存数值取决于货品数量</div
-                  >
-                </el-form-item> -->
+<el-form-item label="商品库存" prop="stock" required>
+  <el-input v-model="formData.stock" placeholder="请输入商品库存" type="number" min="0" />
+  <div class="form-tip">该设置只对单品有效,当商品存在多规格货品时为不可编辑状态,库存数值取决于货品数量</div>
+</el-form-item> -->
 
                 <!-- <el-form-item label="库存预警值" prop="stockThreshold">
                   <el-input
@@ -128,57 +93,36 @@
                   />
                 </el-form-item> -->
 
-                <el-form-item label="商品状态" prop="isShow" required>
+                <el-form-item :label="t('modules.goods.goodsStatus')" prop="isShow" required>
                   <el-radio-group v-model="formData.isShow" :disabled="!isEdit">
-                    <el-radio :label="1">上架</el-radio>
-                    <el-radio :label="0">下架</el-radio>
+                    <el-radio :label="1">{{ t('modules.goods.onSale') }}</el-radio>
+                    <el-radio :label="0">{{ t('modules.goods.offSale') }}</el-radio>
                   </el-radio-group>
-                  <div v-if="!isEdit" class="form-tip ml-20px mb-6px"
-                    >新增商品默认为下架状态,保存后可修改</div
-                  >
+                  <div v-if="!isEdit" class="form-tip ml-20px mb-6px">
+                    {{ t('modules.goods.newGoodsOffSaleTip') }}
+                  </div>
                 </el-form-item>
 
-                <el-form-item label="商品供应商" prop="itemSupplier" required>
-                  <el-input v-model="formData.itemSupplier" placeholder="请输入商品供应商" />
+                <el-form-item :label="t('modules.goods.goodsSupplier')" prop="itemSupplier" required>
+                  <el-input v-model="formData.itemSupplier" :placeholder="t('form.inputGoodsSupplier')" />
                 </el-form-item>
               </el-col>
 
               <!-- 右侧图片上传 -->
               <el-col :span="10">
-                <el-form-item label="白底主图" prop="image" required>
-                  <sa-upload-image
-                    v-model="formData.image"
-                    :max-count="1"
-                    :accept="['jpg', 'jpeg', 'png']"
-                    :max-size="5"
-                    :direct-upload="true"
-                    :size="100"
-                    placeholder="上传白底主图"
-                  />
+                <el-form-item :label="t('modules.goods.mainImage')" prop="image" required>
+                  <sa-upload-image v-model="formData.image" :max-count="1" :accept="['jpg', 'jpeg', 'png']"
+                    :max-size="5" :direct-upload="true" :size="100" :placeholder="t('form.uploadMainImage')" />
                 </el-form-item>
 
-                <el-form-item label="轮播图" prop="sliderImage" required>
-                  <sa-upload-image
-                    v-model="formData.sliderImage"
-                    :max-count="5"
-                    :accept="['jpg', 'jpeg', 'png']"
-                    :max-size="5"
-                    :direct-upload="true"
-                    :size="100"
-                    placeholder="上传轮播图"
-                  />
+                <el-form-item :label="t('modules.goods.carouselImages')" prop="sliderImage" required>
+                  <sa-upload-image v-model="formData.sliderImage" :max-count="5" :accept="['jpg', 'jpeg', 'png']"
+                    :max-size="5" :direct-upload="true" :size="100" :placeholder="t('form.uploadCarouselImages')" />
                 </el-form-item>
 
-                <el-form-item label="详情图" prop="flatPattern" required>
-                  <sa-upload-image
-                    v-model="formData.flatPattern"
-                    :max-count="10"
-                    :accept="['jpg', 'jpeg', 'png']"
-                    :max-size="5"
-                    :direct-upload="true"
-                    :size="100"
-                    placeholder="上传详情图"
-                  />
+                <el-form-item :label="t('modules.goods.detailImages')" prop="flatPattern" required>
+                  <sa-upload-image v-model="formData.flatPattern" :max-count="10" :accept="['jpg', 'jpeg', 'png']"
+                    :max-size="5" :direct-upload="true" :size="100" :placeholder="t('form.uploadDetailImages')" />
                 </el-form-item>
               </el-col>
             </el-row>
@@ -187,143 +131,101 @@
 
         <!-- 第二步:商品属性 -->
         <div v-show="currentStep === 1" class="" :key="forceUpdateKey">
-          <el-form ref="attrFormRef" :model="formData" :rules="attrRules" label-width="120px">
+          <el-form ref="attrFormRef" :model="formData" :rules="attrRules" :label-width="formLabelWidth">
             <!-- 多规格设置 -->
             <el-card class="spec-card">
               <template #header>
                 <div class="card-header">
-                  <span>商品规格设置</span>
+                  <span>{{ t('modules.goods.specificationSettings') }}</span>
                 </div>
               </template>
 
               <!-- 操作 -->
               <div class="sku-wrap" :key="`sku-wrap-${forceUpdateKey}`">
-                <div
-                  class="sku"
-                  v-for="(s, k) in formData.skus"
-                  :key="`sku-${k}-${forceUpdateKey}`"
-                >
+                <div class="sku" v-for="(s, k) in formData.skus" :key="`sku-${k}-${forceUpdateKey}`">
                   <div class="sku-key sa-flex sa-row-between">
                     <div class="sa-flex">
-                      <div class="sa-m-r-16">规格名称</div>
-                      <el-input
-                        v-model="s.name"
-                        placeholder="请输入规格名称"
-                        class="sku-key-input"
-                        @input="buildSkuPriceTable"
-                      ></el-input>
+                      <div class="sa-m-r-16">{{ t('modules.goods.specificationName') }}</div>
+                      <el-input v-model="s.name" :placeholder="t('form.inputSpecificationName')" class="sku-key-input"
+                        @input="buildSkuPriceTable"></el-input>
                     </div>
                     <el-icon @click="deleteMainSku(k)" class="sku-key-icon">
                       <CircleCloseFilled />
                     </el-icon>
                   </div>
                   <div class="sku-value sa-flex sa-flex-wrap">
-                    <div class="sku-value-title sa-m-r-16 sa-m-b-16 sa-flex"> 规格值 </div>
+                    <div class="sku-value-title sa-m-r-16 sa-m-b-16 sa-flex">{{
+                      t('modules.goods.specificationValue')
+                    }}</div>
                     <div v-for="(sc, c) in s.children" :key="c" class="sku-value-box sa-m-b-16">
                       <div class="sku-value-content">
-                        <el-input
-                          v-model="sc.name"
-                          placeholder="请输入规格值"
-                          class="sku-value-input"
-                          @input="buildSkuPriceTable"
-                        ></el-input>
+                        <el-input v-model="sc.name" :placeholder="t('form.inputSpecificationValue')"
+                          class="sku-value-input" @input="buildSkuPriceTable"></el-input>
                         <div class="sku-value-image sa-m-l-8">
-                          <sa-upload-image
-                            v-model="sc.imageList"
-                            :max-count="1"
-                            :accept="['jpg', 'jpeg', 'png']"
-                            :max-size="5"
-                            :direct-upload="true"
-                            :size="30"
-                            :show-tip="false"
-                            :compact="true"
-                            placeholder=""
-                          />
+                          <sa-upload-image v-model="sc.imageList" :max-count="1" :accept="['jpg', 'jpeg', 'png']"
+                            :max-size="5" :direct-upload="true" :size="30" :show-tip="false" :compact="true"
+                            placeholder="" />
                         </div>
                       </div>
                       <el-icon @click="deleteChildrenSku(k, c)" class="sku-value-icon">
                         <CircleCloseFilled />
                       </el-icon>
                     </div>
-                    <div
-                      @click="addChildrenSku(k)"
-                      class="sku-value-add sa-m-r-24 sa-m-b-16 sa-flex cursor-pointer"
-                    >
-                      添加规格值
+                    <div @click="addChildrenSku(k)" class="sku-value-add sa-m-r-24 sa-m-b-16 sa-flex cursor-pointer">
+                      {{ t('modules.goods.addSpecificationValue') }}
                     </div>
                   </div>
                 </div>
                 <div class="sku-tools sa-flex">
-                  <el-button type="primary" class="add" @click="addMainSku">+ 添加规格</el-button>
+                  <el-button type="primary" class="add" @click="addMainSku">+ {{ t('modules.goods.addSpecification')
+                  }}</el-button>
                 </div>
               </div>
 
               <!-- 批量设置 -->
               <div class="sa-m-t-20" v-if="formData.sku_prices.length > 0">
-                <el-form-item label="批量设置" label-width="80px">
+                <el-form-item :label="t('modules.goods.batchSettings')" label-width="110px">
                   <div class="sku sa-m-r-20" v-for="(item, index) in formData.skus" :key="index">
-                    <el-select
-                      v-model="item.batchId"
-                      placeholder="请选择规格"
-                      class="sa-w-150"
-                      clearable
-                    >
+                    <el-select v-model="item.batchId" :placeholder="t('form.selectSpecification')" class="sa-w-150"
+                      clearable>
                       <template v-for="(citem, cindex) in item.children">
-                        <el-option
-                          :key="cindex"
-                          :label="citem.name"
-                          :value="citem.temp_id"
-                          v-if="citem.temp_id && citem.name"
-                        ></el-option>
+                        <el-option :key="cindex" :label="citem.name" :value="citem.temp_id"
+                          v-if="citem.temp_id && citem.name"></el-option>
                       </template>
                     </el-select>
                   </div>
                   <div class="warning-title" style="margin-left: 8px">
-                    未选择规格默认为全选批量设置
+                    {{ t('modules.goods.batchSettingsTip') }}
                   </div>
                 </el-form-item>
                 <div class="sa-flex sa-flex-wrap">
-                  <el-select
-                    v-model="allEditObj.price"
-                    placeholder="请选择售价(৳)"
-                    class="sa-w-200 sa-m-r-10 sa-m-b-10"
-                    clearable
-                  >
+                  <el-select v-model="allEditObj.price" :placeholder="t('form.selectSalePrice')"
+                    class="sa-w-200 sa-m-r-10 sa-m-b-10" clearable>
                     <el-option :value="300" label="300৳" />
                     <el-option :value="500" label="500৳" />
                     <el-option :value="1000" label="1000৳" />
                     <el-option :value="2000" label="2000৳" />
                     <el-option :value="3000" label="3000৳" />
                   </el-select>
-                  <el-select
-                    v-model="allEditObj.otPrice"
-                    placeholder="请选择市场价(৳)"
-                    class="sa-w-200 sa-m-r-10 sa-m-b-10"
-                    clearable
-                  >
+                  <el-select v-model="allEditObj.otPrice" :placeholder="t('form.selectMarketPrice')"
+                    class="sa-w-200 sa-m-r-10 sa-m-b-10" clearable>
                     <el-option :value="300" label="300৳" />
                     <el-option :value="500" label="500৳" />
                     <el-option :value="1000" label="1000৳" />
                     <el-option :value="2000" label="2000৳" />
                     <el-option :value="3000" label="3000৳" />
                   </el-select>
-                  <el-input
-                    v-model="allEditObj.stock"
-                    placeholder="请输入库存"
-                    class="sa-w-250 sa-m-r-10 sa-m-b-10"
-                  >
-                    <template #prepend>库存</template>
+                  <el-input v-model="allEditObj.stock" :placeholder="t('form.inputStock')"
+                    class="sa-w-250 sa-m-r-10 sa-m-b-10">
+                    <template #prepend>{{ t('modules.goods.goodsStock') }}</template>
                   </el-input>
-                  <el-input
-                    v-model="allEditObj.stockThreshold"
-                    placeholder="请输入库存预警值"
-                    class="sa-w-300 sa-m-r-10 sa-m-b-10"
-                  >
-                    <template #prepend>库存预警值</template>
+                  <el-input v-model="allEditObj.stockThreshold" :placeholder="t('form.inputStockThreshold')"
+                    class="sa-w-300 sa-m-r-10 sa-m-b-10">
+                    <template #prepend>{{ t('modules.goods.stockThreshold') }}</template>
                   </el-input>
-                  <el-button type="primary" @click="batchEdit" class="sa-m-b-10"
-                    >批量设置</el-button
-                  >
+                  <el-button type="primary" @click="batchEdit" class="sa-m-b-10">
+                    {{ t('modules.goods.batchSettings') }}
+                  </el-button>
                 </div>
               </div>
 
@@ -335,13 +237,13 @@
                       <template v-for="(item, i) in formData.skus" :key="i">
                         <th v-if="item.children.length">{{ item.name }}</th>
                       </template>
-                      <th>图片</th>
-                      <th><span class="required">*</span>销售价格(৳)</th>
-                      <th><span class="required">*</span>市场价(৳)</th>
-                      <th><span class="required">*</span>商品库存</th>
-                      <th>库存预警值</th>
-                      <th>SKU编码</th>
-                      <th>操作</th>
+                      <th>{{ t('modules.goods.image') }}</th>
+                      <th><span class="required">*</span>{{ t('modules.goods.salePrice') }}(৳)</th>
+                      <th><span class="required">*</span>{{ t('modules.goods.marketPrice') }}(৳)</th>
+                      <th><span class="required">*</span>{{ t('modules.goods.goodsStock') }}</th>
+                      <th>{{ t('modules.goods.stockThreshold') }}</th>
+                      <th>{{ t('modules.goods.skuCode') }}</th>
+                      <th>{{ t('common.actions') }}</th>
                     </tr>
                   </thead>
                   <tbody>
@@ -352,25 +254,13 @@
                         </td>
                       </template>
                       <td class="image">
-                        <sa-upload-image
-                          v-model="item.imageList"
-                          :max-count="1"
-                          :accept="['jpg', 'jpeg', 'png']"
-                          :max-size="5"
-                          :direct-upload="true"
-                          :size="30"
-                          :show-tip="false"
-                          :compact="true"
-                          placeholder=""
-                        />
+                        <sa-upload-image v-model="item.imageList" :max-count="1" :accept="['jpg', 'jpeg', 'png']"
+                          :max-size="5" :direct-upload="true" :size="30" :show-tip="false" :compact="true"
+                          placeholder="" />
                       </td>
                       <td>
-                        <el-select
-                          v-model="item.price"
-                          placeholder="选择价格"
-                          size="small"
-                          :class="{ 'is-error': !item.price || item.price <= 0 }"
-                        >
+                        <el-select v-model="item.price" :placeholder="t('form.selectPrice')" size="small"
+                          :class="{ 'is-error': !item.price || item.price <= 0 }">
                           <el-option :value="300" label="300(৳)" />
                           <el-option :value="500" label="500(৳)" />
                           <el-option :value="1000" label="1000(৳)" />
@@ -379,12 +269,8 @@
                         </el-select>
                       </td>
                       <td>
-                        <el-select
-                          v-model="item.otPrice"
-                          placeholder="选择市场价"
-                          size="small"
-                          :class="{ 'is-error': !item.otPrice || item.otPrice <= 0 }"
-                        >
+                        <el-select v-model="item.otPrice" :placeholder="t('form.selectMarketPrice')" size="small"
+                          :class="{ 'is-error': !item.otPrice || item.otPrice <= 0 }">
                           <el-option :value="300" label="300(৳)" />
                           <el-option :value="500" label="500(৳)" />
                           <el-option :value="1000" label="1000(৳)" />
@@ -393,37 +279,20 @@
                         </el-select>
                       </td>
                       <td class="stock">
-                        <el-input
-                          v-model="item.stock"
-                          placeholder="请输入库存"
-                          size="small"
-                          type="number"
-                          :step="1"
-                          :min="0"
-                          :class="{ 'is-error': !item.stock || item.stock < 0 }"
-                        ></el-input>
+                        <el-input v-model="item.stock" :placeholder="t('form.inputStock')" size="small" type="number"
+                          :step="1" :min="0" :class="{ 'is-error': !item.stock || item.stock < 0 }"></el-input>
                       </td>
                       <td class="stockThreshold">
-                        <el-input
-                          v-model="item.stockThreshold"
-                          placeholder="请输入预警值"
-                          size="small"
-                          type="number"
-                          :step="1"
-                          :min="0"
-                        ></el-input>
+                        <el-input v-model="item.stockThreshold" :placeholder="t('form.inputStockThreshold')"
+                          size="small" type="number" :step="1" :min="0"></el-input>
                       </td>
                       <td class="sn">
-                        <el-input
-                          v-model="item.skuCode"
-                          placeholder="请输入SKU编码"
-                          size="small"
-                        ></el-input>
+                        <el-input v-model="item.skuCode" :placeholder="t('form.inputSkuCode')" size="small"></el-input>
                       </td>
                       <td>
-                        <el-button type="danger" size="small" text @click="deleteSkuPrice(i)"
-                          >删除</el-button
-                        >
+                        <el-button type="danger" size="small" text @click="deleteSkuPrice(i)">
+                          {{ t('common.delete') }}
+                        </el-button>
                       </td>
                     </tr>
                   </tbody>
@@ -441,18 +310,20 @@
                 <CircleCheck />
               </el-icon>
               <h3 class="mt-20">
-                {{ isEdit ? '保存成功' : '商品添加成功,请等上架' }}
+                {{ isEdit ? t('message.saveSuccess') : t('modules.goods.goodsAddedSuccess') }}
               </h3>
               <div v-if="!isEdit" class="action-buttons mt-30">
-                <el-button type="primary" @click="continueAction"> 继续添加 </el-button>
+                <el-button type="primary" @click="continueAction">{{
+                  t('modules.goods.continueAdd')
+                }}</el-button>
               </div>
             </div>
             <div v-else class="loading-content">
               <el-icon class="loading-icon" :size="80">
                 <Loading />
               </el-icon>
-              <h3>正在保存商品...</h3>
-              <p>请稍候,系统正在处理您的请求</p>
+              <h3>{{ t('modules.goods.savingGoods') }}</h3>
+              <p>{{ t('modules.goods.pleaseWait') }}</p>
             </div>
           </div>
         </div>
@@ -462,1239 +333,1256 @@
     <!-- 操作按钮 -->
     <el-footer class="sa-footer--submit">
       <template v-if="currentStep < 2">
-        <el-button v-if="currentStep > 0" @click="prevStep">上一步</el-button>
+        <el-button v-if="currentStep > 0" @click="prevStep">{{ t('common.previous') }}</el-button>
         <el-button type="primary" @click="nextStep" :loading="currentStep === 1 && submitLoading">
-          {{ currentStep === 1 ? '提交' : '下一步' }}
+          {{ currentStep === 1 ? t('common.submit') : t('common.next') }}
         </el-button>
       </template>
       <template v-if="currentStep === 2">
-        <el-button @click="closeModal">关闭</el-button>
+        <el-button @click="closeModal">{{ t('common.close') }}</el-button>
       </template>
     </el-footer>
   </el-container>
 </template>
 
-<script>
-  import { ref, reactive, onMounted, computed } from 'vue';
-  import { ElMessage } from 'element-plus';
-  import { CircleCheck, CircleCloseFilled, Edit, Loading } from '@element-plus/icons-vue';
-
-  import { api } from '../goods.service';
-  import { NIL } from 'uuid';
-
-  export default {
-    name: 'GoodsEditNew',
-    components: {
-      CircleCheck,
-      CircleCloseFilled,
-      Edit,
-      Loading,
-    },
-  };
-</script>
-
 <script setup>
-  const props = defineProps({
-    modal: {
-      type: Object,
-      default: () => ({}),
-    },
-  });
-
-  const emit = defineEmits(['success', 'modalCallBack']);
-
-  // 当前步骤
-  const currentStep = ref(0);
-
-  // 是否为编辑模式
-  const isEdit = computed(() => props.modal?.params?.type === 'edit');
-
-  // 强制重新渲染的key
-  const forceUpdateKey = ref(0);
-
-  // 表单引用
-  const basicFormRef = ref();
-  const attrFormRef = ref();
-
-  // 表单数据
-  const formData = reactive({
-    // 基本信息
-    id: '', // 商品ID,新增后获得
-    cateId: '',
-    storeName: '',
-    keyword: '',
-    itemBrand: '',
-    storeInfo: '',
-    // tempId: 1, // 运费模板ID,默认1
-    itemNumber: '',
-    price: '',
-    otPrice: '',
-    stock: '',
-    stockThreshold: '',
-    isShow: 0, // 状态 0-未上架 1-上架,新增默认下架
-    itemSupplier: '',
-    sort: 0,
-    cost: '', // 成本价
-    vipPrice: '', // 会员价格
-
-    // 图片 - 数组格式,提交时转换为逗号分隔字符串
-    image: [], // 商品主图
-    sliderImage: [], // 轮播图(白底图)
-    flatPattern: [], // 展示图(详情图)
-    // 规格
-    specType: 1, // 规格 0单 1多
-    skus: [
-      {
-        id: 0,
-        temp_id: 1,
-        name: '',
-        batchId: '',
-        pid: 0,
-        children: [],
-      },
-    ],
-    sku_prices: [],
-  });
-
-  // 分类选项
-  const categoryOptions = ref([]);
-
-  // 表单验证规则
-  const basicRules = {
-    cateId: [{ required: true, message: '请选择商品分类', trigger: 'change' }],
-    storeName: [{ required: true, message: '请输入商品名称', trigger: 'blur' }],
-    // itemBrand: [{ required: true, message: '请输入商品品牌', trigger: 'blur' }],
-    // tempId: [{ required: true, message: '请选择运费模板', trigger: 'change' }],
-    // itemNumber: [{ required: true, message: '请输入商品货号', trigger: 'blur' }],
-    // price: [{ required: true, message: '请输入商品售价', trigger: 'blur' }],
-    // stock: [{ required: true, message: '请输入商品库存', trigger: 'blur' }],
-    isShow: [{ required: true, message: '请选择商品状态', trigger: 'change' }],
-    itemSupplier: [{ required: true, message: '请输入商品供应商', trigger: 'blur' }],
-    image: [{ required: true, message: '请上传商品主图', trigger: 'change' }],
-    sliderImage: [{ required: true, message: '请上传轮播图', trigger: 'change' }],
-    flatPattern: [{ required: true, message: '请上传商品详情图', trigger: 'change' }],
-  };
-
-  const attrRules = {
-    spec_price: [{ required: true, message: '请输入价格', trigger: 'blur' }],
-    spec_stock: [{ required: true, message: '请输入库存', trigger: 'blur' }],
-  };
-
-  // 获取步骤状态
-  const getStepStatus = (step) => {
-    if (step < currentStep.value) return 'finish';
-    if (step === currentStep.value) return 'process';
-    return 'wait';
-  };
-
-  // 多规格相关变量和函数
-  const countId = ref(2);
-  const childrenModal = [];
-  const isResetSku = ref(0);
-
-  // 批量操作相关变量
-  const allEditObj = ref({
-    price: null,
-    otPrice: null,
-    stock: null,
-    stockThreshold: null,
-  });
-
-  // 添加主规格
-  const addMainSku = () => {
-    formData.skus.push({
+import { ref, reactive, onMounted, computed } from 'vue';
+import { ElMessage } from 'element-plus';
+import { CircleCheck, CircleCloseFilled, Edit, Loading } from '@element-plus/icons-vue';
+import { useI18n } from 'vue-i18n';
+import { useFormConfig } from '@/hooks/useFormConfig';
+import { api } from '../goods.service';
+import { NIL } from 'uuid';
+
+const { t } = useI18n();
+
+// 使用表单配置hooks
+const { formLabelWidth, formLayout } = useFormConfig();
+
+const props = defineProps({
+  modal: {
+    type: Object,
+    default: () => ({}),
+  },
+});
+
+const emit = defineEmits(['success', 'modalCallBack']);
+
+// 当前步骤
+const currentStep = ref(0);
+
+// 是否为编辑模式
+const isEdit = computed(() => props.modal?.params?.type === 'edit');
+
+// 强制重新渲染的key
+const forceUpdateKey = ref(0);
+
+// 表单引用
+const basicFormRef = ref();
+const attrFormRef = ref();
+
+// 表单数据
+const formData = reactive({
+  // 基本信息
+  id: '', // 商品ID,新增后获得
+  cateId: '',
+  storeName: '',
+  keyword: '',
+  itemBrand: '',
+  storeInfo: '',
+  // tempId: 1, // 运费模板ID,默认1
+  itemNumber: '',
+  price: '',
+  otPrice: '',
+  stock: '',
+  stockThreshold: '',
+  isShow: 0, // 状态 0-未上架 1-上架,新增默认下架
+  itemSupplier: '',
+  sort: 0,
+  cost: '', // 成本价
+  vipPrice: '', // 会员价格
+
+  // 图片 - 数组格式,提交时转换为逗号分隔字符串
+  image: [], // 商品主图
+  sliderImage: [], // 轮播图(白底图)
+  flatPattern: [], // 展示图(详情图)
+  // 规格
+  specType: 1, // 规格 0单 1多
+  skus: [
+    {
       id: 0,
-      temp_id: countId.value++,
+      temp_id: 1,
       name: '',
       batchId: '',
       pid: 0,
       children: [],
-    });
-    buildSkuPriceTable();
-  };
+    },
+  ],
+  sku_prices: [],
+});
+
+// 分类选项
+const categoryOptions = ref([]);
+
+// 表单验证规则
+const basicRules = computed(() => ({
+  cateId: [{ required: true, message: t('form.categoryRequired'), trigger: 'change' }],
+  storeName: [{ required: true, message: t('form.goodsNameRequired'), trigger: 'blur' }],
+  // itemBrand: [{ required: true, message: t('form.goodsBrandRequired'), trigger: 'blur' }],
+  // tempId: [{ required: true, message: t('form.shippingTemplateRequired'), trigger: 'change' }],
+  // itemNumber: [{ required: true, message: t('form.goodsNumberRequired'), trigger: 'blur' }],
+  // price: [{ required: true, message: t('form.salePriceRequired'), trigger: 'blur' }],
+  // stock: [{ required: true, message: t('form.stockRequired'), trigger: 'blur' }],
+  isShow: [{ required: true, message: t('form.goodsStatusRequired'), trigger: 'change' }],
+  itemSupplier: [{ required: true, message: t('form.goodsSupplierRequired'), trigger: 'blur' }],
+  image: [{ required: true, message: t('form.mainImageRequired'), trigger: 'change' }],
+  sliderImage: [{ required: true, message: t('form.carouselImagesRequired'), trigger: 'change' }],
+  flatPattern: [{ required: true, message: t('form.detailImagesRequired'), trigger: 'change' }],
+}));
+
+const attrRules = {
+  spec_price: [{ required: true, message: '请输入价格', trigger: 'blur' }],
+  spec_stock: [{ required: true, message: '请输入库存', trigger: 'blur' }],
+};
+
+// 获取步骤状态
+const getStepStatus = (step) => {
+  if (step < currentStep.value) return 'finish';
+  if (step === currentStep.value) return 'process';
+  return 'wait';
+};
+
+// 多规格相关变量和函数
+const countId = ref(2);
+const childrenModal = [];
+const isResetSku = ref(0);
+
+// 批量操作相关变量
+const allEditObj = ref({
+  price: null,
+  otPrice: null,
+  stock: null,
+  stockThreshold: null,
+});
+
+// 添加主规格
+const addMainSku = () => {
+  formData.skus.push({
+    id: 0,
+    temp_id: countId.value++,
+    name: '',
+    batchId: '',
+    pid: 0,
+    children: [],
+  });
+  buildSkuPriceTable();
+};
 
-  // 删除主规格
-  const deleteMainSku = (k) => {
-    let data = formData.skus[k];
+// 删除主规格
+const deleteMainSku = (k) => {
+  let data = formData.skus[k];
 
-    // 删除主规格
-    formData.skus.splice(k, 1);
+  // 删除主规格
+  formData.skus.splice(k, 1);
 
-    // 如果当前删除的主规格存在子规格,则清空 skuPrice,不存在子规格则不清空
-    if (data.children.length > 0) {
-      formData.sku_prices = []; // 规格大变化,清空skuPrice
-      isResetSku.value = 1; // 重置规格
+  // 如果当前删除的主规格存在子规格,则清空 skuPrice,不存在子规格则不清空
+  if (data.children.length > 0) {
+    formData.sku_prices = []; // 规格大变化,清空skuPrice
+    isResetSku.value = 1; // 重置规格
+  }
+  buildSkuPriceTable();
+};
+
+// 添加子规格
+const addChildrenSku = (k) => {
+  let isExist = false;
+  formData.skus[k].children.forEach((e) => {
+    if (e.name == childrenModal[k] && e.name != '') {
+      isExist = true;
     }
-    buildSkuPriceTable();
-  };
+  });
+  if (isExist) {
+    ElMessage.warning(t('modules.goods.specificationValueExists'));
+    return false;
+  }
 
-  // 添加子规格
-  const addChildrenSku = (k) => {
-    let isExist = false;
-    formData.skus[k].children.forEach((e) => {
-      if (e.name == childrenModal[k] && e.name != '') {
-        isExist = true;
+  // 修复:确保countId是全局最大的temp_id + 1
+  let maxTempId = 0;
+  formData.skus.forEach((sku) => {
+    sku.children.forEach((child) => {
+      if (child.temp_id > maxTempId) {
+        maxTempId = child.temp_id;
       }
     });
-    if (isExist) {
-      ElMessage.warning('子规格已存在');
-      return false;
-    }
+  });
+  const newTempId = maxTempId + 1;
+
+  formData.skus[k].children.push({
+    id: 0,
+    temp_id: newTempId,
+    name: childrenModal[k] || '',
+    pid: formData.skus[k].id,
+    imageList: [], // 添加图片列表属性
+  });
+  childrenModal[k] = '';
 
-    // 修复:确保countId是全局最大的temp_id + 1
-    let maxTempId = 0;
-    formData.skus.forEach((sku) => {
-      sku.children.forEach((child) => {
-        if (child.temp_id > maxTempId) {
-          maxTempId = child.temp_id;
-        }
-      });
-    });
-    const newTempId = maxTempId + 1;
+  // 更新countId为新的最大值
+  countId.value = newTempId + 1;
 
-    formData.skus[k].children.push({
-      id: 0,
-      temp_id: newTempId,
-      name: childrenModal[k] || '',
-      pid: formData.skus[k].id,
-      imageList: [], // 添加图片列表属性
+  // 如果是添加的第一个子规格,清空 skuPrice
+  if (formData.skus[k].children.length == 1) {
+    formData.sku_prices = []; // 规格大变化,清空skuPrice
+    isResetSku.value = 1; // 重置规格
+  }
+  buildSkuPriceTable();
+};
+
+// 删除子规格
+const deleteChildrenSku = (k, i) => {
+  let data = formData.skus[k].children[i];
+  formData.skus[k].children.splice(i, 1);
+
+  // 查询 sku_prices 中包含被删除的子规格的项,然后移除
+  let deleteArr = [];
+  formData.sku_prices.forEach((item, index) => {
+    item.goods_sku_text.forEach((e, idx) => {
+      if (e == data.name) {
+        deleteArr.push(index);
+      }
     });
-    childrenModal[k] = '';
-
-    // 更新countId为新的最大值
-    countId.value = newTempId + 1;
-
-    // 如果是添加的第一个子规格,清空 skuPrice
-    if (formData.skus[k].children.length == 1) {
-      formData.sku_prices = []; // 规格大变化,清空skuPrice
-      isResetSku.value = 1; // 重置规格
-    }
-    buildSkuPriceTable();
-  };
+  });
+  deleteArr.sort(function (a, b) {
+    return b - a;
+  });
+  // 移除有相关子规格的项
+  deleteArr.forEach((idx, e) => {
+    formData.sku_prices.splice(idx, 1);
+  });
 
-  // 删除子规格
-  const deleteChildrenSku = (k, i) => {
-    let data = formData.skus[k].children[i];
-    formData.skus[k].children.splice(i, 1);
-
-    // 查询 sku_prices 中包含被删除的子规格的项,然后移除
-    let deleteArr = [];
-    formData.sku_prices.forEach((item, index) => {
-      item.goods_sku_text.forEach((e, idx) => {
-        if (e == data.name) {
-          deleteArr.push(index);
-        }
+  // 当前规格项,所有子规格都被删除,清空 sku_prices
+  if (formData.skus[k].children.length <= 0) {
+    formData.sku_prices = []; // 规格大变化,清空skuPrice
+    isResetSku.value = 1; // 重置规格
+  }
+  buildSkuPriceTable();
+};
+
+// 组成新的规格
+const buildSkuPriceTable = () => {
+  let arr = [];
+  // 遍历sku子规格生成新数组,然后执行递归笛卡尔积
+  formData.skus.forEach((s1, k1) => {
+    let children = s1.children;
+    let childrenIdArray = [];
+    if (children.length > 0) {
+      children.forEach((s2, k2) => {
+        childrenIdArray.push(s2.temp_id);
       });
-    });
-    deleteArr.sort(function (a, b) {
-      return b - a;
-    });
-    // 移除有相关子规格的项
-    deleteArr.forEach((idx, e) => {
-      formData.sku_prices.splice(idx, 1);
-    });
-
-    // 当前规格项,所有子规格都被删除,清空 sku_prices
-    if (formData.skus[k].children.length <= 0) {
-      formData.sku_prices = []; // 规格大变化,清空skuPrice
-      isResetSku.value = 1; // 重置规格
+      // 如果 children 子规格数量为 0,则不渲染当前规格
+      arr.push(childrenIdArray);
     }
-    buildSkuPriceTable();
-  };
-
-  // 组成新的规格
-  const buildSkuPriceTable = () => {
-    let arr = [];
-    // 遍历sku子规格生成新数组,然后执行递归笛卡尔积
-    formData.skus.forEach((s1, k1) => {
-      let children = s1.children;
-      let childrenIdArray = [];
-      if (children.length > 0) {
-        children.forEach((s2, k2) => {
-          childrenIdArray.push(s2.temp_id);
-        });
-        // 如果 children 子规格数量为 0,则不渲染当前规格
-        arr.push(childrenIdArray);
-      }
-    });
-
-    recursionSku(arr, 0, []);
-  };
+  });
 
-  // 递归找笛卡尔规格集合
-  const recursionSku = (arr, k, temp) => {
-    if (k == arr.length && k != 0) {
-      let tempDetail = [];
-      let tempDetailIds = [];
-
-      // 修复:每个temp_id只应该匹配一次,找到就跳出
-      temp.forEach((item) => {
-        let found = false;
-        for (let sku of formData.skus) {
-          if (found) break;
-          for (let child of sku.children) {
-            if (item == child.temp_id) {
-              tempDetail.push(child.name);
-              tempDetailIds.push(child.temp_id);
-              found = true;
-              break;
-            }
+  recursionSku(arr, 0, []);
+};
+
+// 递归找笛卡尔规格集合
+const recursionSku = (arr, k, temp) => {
+  if (k == arr.length && k != 0) {
+    let tempDetail = [];
+    let tempDetailIds = [];
+
+    // 修复:每个temp_id只应该匹配一次,找到就跳出
+    temp.forEach((item) => {
+      let found = false;
+      for (let sku of formData.skus) {
+        if (found) break;
+        for (let child of sku.children) {
+          if (item == child.temp_id) {
+            tempDetail.push(child.name);
+            tempDetailIds.push(child.temp_id);
+            found = true;
+            break;
           }
         }
-      });
-
-      let flag = false; // 默认添加新的
-      for (let i = 0; i < formData.sku_prices.length; i++) {
-        if (formData.sku_prices[i].goods_sku_temp_ids.join(',') == tempDetailIds.join(',')) {
-          flag = i;
-          break;
-        }
       }
+    });
 
-      if (flag === false) {
-        formData.sku_prices.push({
-          id: 0,
-          temp_id: formData.sku_prices.length + 1,
-          goods_sku_ids: '',
-          goods_id: 0,
-          weigh: 0,
-          image: '',
-          imageList: [],
-          stock: 0,
-          stockThreshold: 0,
-          price: 0,
-          otPrice: 0,
-          skuCode: '',
-          goods_sku_text: tempDetail,
-          goods_sku_temp_ids: tempDetailIds,
-        });
-      } else {
-        formData.sku_prices[flag].goods_sku_text = tempDetail;
-        formData.sku_prices[flag].goods_sku_temp_ids = tempDetailIds;
+    let flag = false; // 默认添加新的
+    for (let i = 0; i < formData.sku_prices.length; i++) {
+      if (formData.sku_prices[i].goods_sku_temp_ids.join(',') == tempDetailIds.join(',')) {
+        flag = i;
+        break;
       }
-      return;
     }
-    if (arr.length) {
-      for (let i = 0; i < arr[k].length; i++) {
-        temp[k] = arr[k][i];
-        recursionSku(arr, k + 1, temp);
-      }
+
+    if (flag === false) {
+      formData.sku_prices.push({
+        id: 0,
+        temp_id: formData.sku_prices.length + 1,
+        goods_sku_ids: '',
+        goods_id: 0,
+        weigh: 0,
+        image: '',
+        imageList: [],
+        stock: 0,
+        stockThreshold: 0,
+        price: 0,
+        otPrice: 0,
+        skuCode: '',
+        goods_sku_text: tempDetail,
+        goods_sku_temp_ids: tempDetailIds,
+      });
+    } else {
+      formData.sku_prices[flag].goods_sku_text = tempDetail;
+      formData.sku_prices[flag].goods_sku_temp_ids = tempDetailIds;
     }
-  };
+    return;
+  }
+  if (arr.length) {
+    for (let i = 0; i < arr[k].length; i++) {
+      temp[k] = arr[k][i];
+      recursionSku(arr, k + 1, temp);
+    }
+  }
+};
+
+// 批量操作
+const batchEdit = () => {
+  const batchIds = formData.skus.map((item) => item.batchId).filter((item) => Boolean(item));
+  formData.sku_prices.forEach((item) => {
+    if (
+      batchIds.length ? batchIds.every((citem) => item.goods_sku_temp_ids.includes(citem)) : true
+    ) {
+      const { price, otPrice, stock, stockThreshold } = allEditObj.value;
+      if (price) item.price = price;
+      if (otPrice) item.otPrice = otPrice;
+      if (stock) item.stock = stock;
+      if (stockThreshold) item.stockThreshold = stockThreshold;
+    }
+  });
 
-  // 批量操作
-  const batchEdit = () => {
-    const batchIds = formData.skus.map((item) => item.batchId).filter((item) => Boolean(item));
-    formData.sku_prices.forEach((item) => {
-      if (
-        batchIds.length ? batchIds.every((citem) => item.goods_sku_temp_ids.includes(citem)) : true
-      ) {
-        const { price, otPrice, stock, stockThreshold } = allEditObj.value;
-        if (price) item.price = price;
-        if (otPrice) item.otPrice = otPrice;
-        if (stock) item.stock = stock;
-        if (stockThreshold) item.stockThreshold = stockThreshold;
-      }
-    });
+  // 清空输入框
+  allEditObj.value = {
+    price: null,
+    otPrice: null,
+    stock: null,
+    stockThreshold: null,
+  };
 
-    // 清空输入框
-    allEditObj.value = {
-      price: null,
-      otPrice: null,
-      stock: null,
-      stockThreshold: null,
-    };
+  // 清空选择的规格
+  formData.skus.forEach((item) => {
+    item.batchId = '';
+  });
 
-    // 清空选择的规格
-    formData.skus.forEach((item) => {
-      item.batchId = '';
-    });
+  ElMessage.success(t('modules.goods.batchSettingsSuccess'));
+};
 
-    ElMessage.success('批量设置成功');
-  };
+// 删除规格组合
+const deleteSkuPrice = (index) => {
+  formData.sku_prices.splice(index, 1);
+  ElMessage.success(t('message.deleteSuccess'));
+};
 
-  // 删除规格组合
-  const deleteSkuPrice = (index) => {
-    formData.sku_prices.splice(index, 1);
-    ElMessage.success('删除成功');
-  };
+// SKU校验
+const validateSku = () => {
+  if (formData.sku_prices.length === 0) {
+    ElMessage.error(t('modules.goods.pleaseAddSpecification'));
+    return false;
+  }
 
-  // SKU校验
-  const validateSku = () => {
-    if (formData.sku_prices.length === 0) {
-      ElMessage.error('请先添加商品规格');
+  for (let i = 0; i < formData.sku_prices.length; i++) {
+    const item = formData.sku_prices[i];
+    if (!item.price || item.price <= 0) {
+      ElMessage.error(t('modules.goods.specificationSalePriceError', { index: i + 1 }));
       return false;
     }
-
-    for (let i = 0; i < formData.sku_prices.length; i++) {
-      const item = formData.sku_prices[i];
-      if (!item.price || item.price <= 0) {
-        ElMessage.error(`第${i + 1}个规格的销售价格不能为空且必须大于0`);
-        return false;
-      }
-      if (!item.otPrice || item.otPrice <= 0) {
-        ElMessage.error(`第${i + 1}个规格的市场价不能为空且必须大于0`);
-        return false;
-      }
-      if (item.stock === null || item.stock === undefined || item.stock < 0) {
-        ElMessage.error(`第${i + 1}个规格的商品库存不能为空且不能小于0`);
-        return false;
-      }
+    if (!item.otPrice || item.otPrice <= 0) {
+      ElMessage.error(t('modules.goods.specificationMarketPriceError', { index: i + 1 }));
+      return false;
     }
-    return true;
-  };
-
-  // 提交表单
-  const submitLoading = ref(false);
-  const submitError = ref('');
-  const submitSuccess = ref(false);
-
-  // 数据转换工具方法
-  const dataConverter = {
-    // 图片数组转换为逗号分隔字符串
-    imagesToString: (imageArray) => {
-      return Array.isArray(imageArray) ? imageArray.join(',') : '';
-    },
-
-    // 图片字符串转换为数组
-    stringToImages: (imageString) => {
-      if (!imageString) return [];
-      return imageString.split(',').filter((img) => img.trim());
-    },
+    if (item.stock === null || item.stock === undefined || item.stock < 0) {
+      ElMessage.error(t('modules.goods.specificationStockError', { index: i + 1 }));
+      return false;
+    }
+  }
+  return true;
+};
+
+// 提交表单
+const submitLoading = ref(false);
+const submitError = ref('');
+const submitSuccess = ref(false);
+
+// 数据转换工具方法
+const dataConverter = {
+  // 图片数组转换为逗号分隔字符串
+  imagesToString: (imageArray) => {
+    return Array.isArray(imageArray) ? imageArray.join(',') : '';
+  },
+
+  // 图片字符串转换为数组
+  stringToImages: (imageString) => {
+    if (!imageString) return [];
+    return imageString.split(',').filter((img) => img.trim());
+  },
+
+  // 将后端返回的 attr 和 attrValue 数据转换为前端需要的格式
+  convertBackendToFrontend: (attr, attrValue) => {
+    const skus = [];
+    const sku_prices = [];
+    let tempId = 1;
+
+    // 处理 attr 数据,构建 skus 结构
+    if (attr && attr.length > 0) {
+      attr.forEach((attrItem) => {
+        // 处理规格值图片数据
+        const attrImgMap = {};
+        if (attrItem.attrImgValues && Array.isArray(attrItem.attrImgValues)) {
+          attrItem.attrImgValues.forEach((imgItem) => {
+            attrImgMap[imgItem.name] = imgItem.img ? [imgItem.img] : [];
+          });
+        }
 
-    // 将后端返回的 attr 和 attrValue 数据转换为前端需要的格式
-    convertBackendToFrontend: (attr, attrValue) => {
-      const skus = [];
-      const sku_prices = [];
-      let tempId = 1;
-
-      // 处理 attr 数据,构建 skus 结构
-      if (attr && attr.length > 0) {
-        attr.forEach((attrItem) => {
-          // 处理规格值图片数据
-          const attrImgMap = {};
-          if (attrItem.attrImgValues && Array.isArray(attrItem.attrImgValues)) {
-            attrItem.attrImgValues.forEach((imgItem) => {
-              attrImgMap[imgItem.name] = imgItem.img ? [imgItem.img] : [];
-            });
-          }
+        const children = attrItem.attrValues.split(',').map((value) => ({
+          id: 0, // 子规格值没有单独的ID,保持为0
+          temp_id: tempId++,
+          name: value.trim(),
+          pid: attrItem.id, // 使用父规格的ID
+          imageList: attrImgMap[value.trim()] || [], // 添加图片列表
+        }));
 
-          const children = attrItem.attrValues.split(',').map((value) => ({
-            id: 0, // 子规格值没有单独的ID,保持为0
-            temp_id: tempId++,
-            name: value.trim(),
-            pid: attrItem.id, // 使用父规格的ID
-            imageList: attrImgMap[value.trim()] || [], // 添加图片列表
-          }));
-
-          skus.push({
-            id: attrItem.id || 0, // 保留原有的规格ID
-            temp_id: tempId++,
-            name: attrItem.attrName,
-            batchId: '',
-            pid: 0,
-            children: children,
-          });
+        skus.push({
+          id: attrItem.id || 0, // 保留原有的规格ID
+          temp_id: tempId++,
+          name: attrItem.attrName,
+          batchId: '',
+          pid: 0,
+          children: children,
         });
-      }
+      });
+    }
 
-      // 处理 attrValue 数据,构建 sku_prices 结构
-      if (attrValue && attrValue.length > 0) {
-        // 按照attr的顺序对attrValue进行排序
-        const sortedAttrValue = [...attrValue].sort((a, b) => {
-          try {
-            const aAttrObj = JSON.parse(a.attrValue || '{}');
-            const bAttrObj = JSON.parse(b.attrValue || '{}');
-
-            // 按照attr的顺序比较每个属性值
-            for (let i = 0; i < attr.length; i++) {
-              const attrName = attr[i].attrName;
-              const aValue = aAttrObj[attrName];
-              const bValue = bAttrObj[attrName];
-
-              if (aValue && bValue) {
-                // 找到该属性值在attrValues中的索引
-                const attrValues = attr[i].attrValues.split(',').map((v) => v.trim());
-                const aIndex = attrValues.indexOf(aValue);
-                const bIndex = attrValues.indexOf(bValue);
-
-                if (aIndex !== bIndex) {
-                  return aIndex - bIndex;
-                }
+    // 处理 attrValue 数据,构建 sku_prices 结构
+    if (attrValue && attrValue.length > 0) {
+      // 按照attr的顺序对attrValue进行排序
+      const sortedAttrValue = [...attrValue].sort((a, b) => {
+        try {
+          const aAttrObj = JSON.parse(a.attrValue || '{}');
+          const bAttrObj = JSON.parse(b.attrValue || '{}');
+
+          // 按照attr的顺序比较每个属性值
+          for (let i = 0; i < attr.length; i++) {
+            const attrName = attr[i].attrName;
+            const aValue = aAttrObj[attrName];
+            const bValue = bAttrObj[attrName];
+
+            if (aValue && bValue) {
+              // 找到该属性值在attrValues中的索引
+              const attrValues = attr[i].attrValues.split(',').map((v) => v.trim());
+              const aIndex = attrValues.indexOf(aValue);
+              const bIndex = attrValues.indexOf(bValue);
+
+              if (aIndex !== bIndex) {
+                return aIndex - bIndex;
               }
             }
-            return 0;
-          } catch (e) {
-            return 0;
           }
-        });
+          return 0;
+        } catch (e) {
+          return 0;
+        }
+      });
 
-        sortedAttrValue.forEach((item) => {
-          let attrValueObj = {};
-          try {
-            attrValueObj = JSON.parse(item.attrValue || '{}');
-          } catch (e) {
-            console.warn('解析 attrValue 失败:', item.attrValue, e);
-          }
+      sortedAttrValue.forEach((item) => {
+        let attrValueObj = {};
+        try {
+          attrValueObj = JSON.parse(item.attrValue || '{}');
+        } catch (e) {
+          console.warn('解析 attrValue 失败:', item.attrValue, e);
+        }
 
-          // 构建 goods_sku_text 和 goods_sku_temp_ids
-          const goods_sku_text = [];
-          const goods_sku_temp_ids = [];
-
-          // 根据 skus 的顺序构建 goods_sku_text
-          skus.forEach((sku) => {
-            const value = attrValueObj[sku.name];
-            if (value) {
-              goods_sku_text.push(value);
-              // 找到对应的 temp_id
-              const child = sku.children.find((c) => c.name === value);
-              if (child) {
-                goods_sku_temp_ids.push(child.temp_id);
-              }
+        // 构建 goods_sku_text 和 goods_sku_temp_ids
+        const goods_sku_text = [];
+        const goods_sku_temp_ids = [];
+
+        // 根据 skus 的顺序构建 goods_sku_text
+        skus.forEach((sku) => {
+          const value = attrValueObj[sku.name];
+          if (value) {
+            goods_sku_text.push(value);
+            // 找到对应的 temp_id
+            const child = sku.children.find((c) => c.name === value);
+            if (child) {
+              goods_sku_temp_ids.push(child.temp_id);
             }
-          });
-
-          sku_prices.push({
-            id: parseInt(item.id) || 0, // 保留原有的SKU价格ID
-            temp_id: sku_prices.length + 1,
-            goods_sku_ids: '',
-            goods_id: parseInt(item.productId) || 0,
-            weigh: 0,
-            image: item.image || '',
-            imageList: item.image ? [item.image] : [],
-            stock: parseInt(item.stock) || 0,
-            stockThreshold: parseInt(item.stockThreshold) || 0,
-            price: parseFloat(item.price) || 0,
-            otPrice: parseFloat(item.otPrice) || 0,
-            skuCode: item.skuCode || item.barCode || item.suk || '',
-            goods_sku_text: goods_sku_text,
-            goods_sku_temp_ids: goods_sku_temp_ids,
-          });
-        });
-      }
-
-      return { skus, sku_prices };
-    },
-
-    // 将前端数据转换为后端需要的格式
-    convertFrontendToBackend: (skus, sku_prices) => {
-      // 生成 attr 数据
-      const attr = skus
-        .filter((sku) => sku.name && sku.children.length > 0)
-        .map((sku) => ({
-          id: sku.id || 0, // 保留规格ID,新增时为0
-          attrName: sku.name,
-          attrValues: sku.children.map((child) => child.name).join(','),
-          // 生成 attrImgValues 数据 - 为所有规格值生成结构,没有图片时img为空字符串
-          attrImgValues: sku.children.map((child) => ({
-            name: child.name,
-            img: child.imageList && child.imageList.length > 0 ? child.imageList[0] : '', // 有图片取第一张,没有则为空字符串
-          })),
-        }));
-
-      // 生成 attrValue 数据
-      const attrValue = sku_prices.map((item) => {
-        const attrObj = {};
-        const attrValueObj = {};
-
-        // 根据 goods_sku_text 和对应的规格名称构建属性对象
-        item.goods_sku_text.forEach((value, index) => {
-          const specName = skus[index]?.name;
-          if (specName) {
-            attrObj[specName] = value;
-            attrValueObj[specName] = value;
           }
         });
 
-        return {
-          id: item.id || 0, // 保留SKU价格ID,新增时为0
-          image: item.imageList && item.imageList.length > 0 ? item.imageList[0] : '',
-          price: item.price || '0',
-          otPrice: item.otPrice || '0',
-          stock: item.stock || 0,
-          skuCode: item.skuCode || '',
-          stockThreshold: item.stockThreshold || 0,
-          attrValue: JSON.stringify(attrValueObj),
-          ...attrObj,
-          productId: item.goods_id || 0,
-        };
+        sku_prices.push({
+          id: parseInt(item.id) || 0, // 保留原有的SKU价格ID
+          temp_id: sku_prices.length + 1,
+          goods_sku_ids: '',
+          goods_id: parseInt(item.productId) || 0,
+          weigh: 0,
+          image: item.image || '',
+          imageList: item.image ? [item.image] : [],
+          stock: parseInt(item.stock) || 0,
+          stockThreshold: parseInt(item.stockThreshold) || 0,
+          price: parseFloat(item.price) || 0,
+          otPrice: parseFloat(item.otPrice) || 0,
+          skuCode: item.skuCode || item.barCode || item.suk || '',
+          goods_sku_text: goods_sku_text,
+          goods_sku_temp_ids: goods_sku_temp_ids,
+        });
       });
-
-      return { attr, attrValue };
-    },
-  };
-
-  // 兼容旧方法名
-  const convertImagesToString = dataConverter.imagesToString;
-  const convertStringToImages = dataConverter.stringToImages;
-
-  // 下一步
-  const nextStep = async () => {
-    if (currentStep.value === 0) {
-      // 验证基本信息
-      const valid = await basicFormRef.value?.validate().catch(() => false);
-      if (!valid) {
-        ElMessage.error('请完善基本信息');
-        return;
-      }
-      // 进入下一步(商品属性设置)
-      currentStep.value++;
-    } else if (currentStep.value === 1) {
-      // 第二步:提交所有数据(基本信息 + 商品属性)
-      await submitGoods();
     }
-  };
 
-  // 继续操作(只用于新增模式)
-  const continueAction = () => {
-    // 新增模式:重置表单继续添加
-    resetForm();
-    currentStep.value = 0;
-    submitSuccess.value = false;
-  };
-
-  // 关闭弹窗
-  const closeModal = () => {
-    emit('modalCallBack', {
-      event: 'confirm',
-    });
-  };
+    return { skus, sku_prices };
+  },
+
+  // 将前端数据转换为后端需要的格式
+  convertFrontendToBackend: (skus, sku_prices) => {
+    // 生成 attr 数据
+    const attr = skus
+      .filter((sku) => sku.name && sku.children.length > 0)
+      .map((sku) => ({
+        id: sku.id || 0, // 保留规格ID,新增时为0
+        attrName: sku.name,
+        attrValues: sku.children.map((child) => child.name).join(','),
+        // 生成 attrImgValues 数据 - 为所有规格值生成结构,没有图片时img为空字符串
+        attrImgValues: sku.children.map((child) => ({
+          name: child.name,
+          img: child.imageList && child.imageList.length > 0 ? child.imageList[0] : '', // 有图片取第一张,没有则为空字符串
+        })),
+      }));
+
+    // 生成 attrValue 数据
+    const attrValue = sku_prices.map((item) => {
+      const attrObj = {};
+      const attrValueObj = {};
+
+      // 根据 goods_sku_text 和对应的规格名称构建属性对象
+      item.goods_sku_text.forEach((value, index) => {
+        const specName = skus[index]?.name;
+        if (specName) {
+          attrObj[specName] = value;
+          attrValueObj[specName] = value;
+        }
+      });
 
-  // 重置表单
-  const resetForm = () => {
-    Object.assign(formData, {
-      title: '',
-      category_ids: [],
-      brand: '',
-      goods_no: '',
-      supplier: '',
-      images: [],
-      white_bg_image: [],
-      detail_images: [],
-      has_spec: true,
-      skus: [
-        {
-          id: 0,
-          temp_id: 1,
-          name: '',
-          batchId: '',
-          pid: 0,
-          children: [],
-        },
-      ],
-      sku_prices: [],
+      return {
+        id: item.id || 0, // 保留SKU价格ID,新增时为0
+        image: item.imageList && item.imageList.length > 0 ? item.imageList[0] : '',
+        price: item.price || '0',
+        otPrice: item.otPrice || '0',
+        stock: item.stock || 0,
+        skuCode: item.skuCode || '',
+        stockThreshold: item.stockThreshold || 0,
+        attrValue: JSON.stringify(attrValueObj),
+        ...attrObj,
+        productId: item.goods_id || 0,
+      };
     });
-  };
 
-  // 上一步
-  const prevStep = () => {
-    if (currentStep.value > 0) {
-      currentStep.value--;
+    return { attr, attrValue };
+  },
+};
+
+// 兼容旧方法名
+const convertImagesToString = dataConverter.imagesToString;
+const convertStringToImages = dataConverter.stringToImages;
+
+// 下一步
+const nextStep = async () => {
+  if (currentStep.value === 0) {
+    // 验证基本信息
+    const valid = await basicFormRef.value?.validate().catch(() => false);
+    if (!valid) {
+      ElMessage.error(t('modules.goods.pleaseCompleteBasicInfo'));
+      return;
     }
-  };
+    // 进入下一步(商品属性设置)
+    currentStep.value++;
+  } else if (currentStep.value === 1) {
+    // 第二步:提交所有数据(基本信息 + 商品属性)
+    await submitGoods();
+  }
+};
+
+// 继续操作(只用于新增模式)
+const continueAction = () => {
+  // 新增模式:重置表单继续添加
+  resetForm();
+  currentStep.value = 0;
+  submitSuccess.value = false;
+};
+
+// 关闭弹窗
+const closeModal = () => {
+  emit('modalCallBack', {
+    event: 'confirm',
+  });
+};
+
+// 重置表单
+const resetForm = () => {
+  Object.assign(formData, {
+    title: '',
+    category_ids: [],
+    brand: '',
+    goods_no: '',
+    supplier: '',
+    images: [],
+    white_bg_image: [],
+    detail_images: [],
+    has_spec: true,
+    skus: [
+      {
+        id: 0,
+        temp_id: 1,
+        name: '',
+        batchId: '',
+        pid: 0,
+        children: [],
+      },
+    ],
+    sku_prices: [],
+  });
+};
 
-  // 保存草稿
-  const saveDraft = async () => {
-    try {
-      const data = { ...formData, status: 'draft' };
-      // 调用保存接口
-      await saveGoods(data);
-      ElMessage.success('保存草稿成功');
-      emit('modalCallBack', { event: 'confirm' });
-    } catch (error) {
-      ElMessage.error('保存失败:' + error.message);
+// 上一步
+const prevStep = () => {
+  if (currentStep.value > 0) {
+    currentStep.value--;
+  }
+};
+
+// 保存草稿
+const saveDraft = async () => {
+  try {
+    const data = { ...formData, status: 'draft' };
+    // 调用保存接口
+    await saveGoods(data);
+    ElMessage.success('保存草稿成功');
+    emit('modalCallBack', { event: 'confirm' });
+  } catch (error) {
+    ElMessage.error('保存失败:' + error.message);
+  }
+};
+
+// 统一提交商品(包含基本信息和属性)
+const submitGoods = async () => {
+  try {
+    submitLoading.value = true;
+    submitError.value = '';
+
+    // 验证基本信息表单
+    const basicValid = await basicFormRef.value?.validate().catch(() => false);
+    if (!basicValid) {
+      ElMessage.error(t('modules.goods.pleaseCompleteBasicInfo'));
+      return;
     }
-  };
 
-  // 统一提交商品(包含基本信息和属性)
-  const submitGoods = async () => {
-    try {
-      submitLoading.value = true;
-      submitError.value = '';
-
-      // 验证基本信息表单
-      const basicValid = await basicFormRef.value?.validate().catch(() => false);
-      if (!basicValid) {
-        ElMessage.error('请完善商品基本信息');
-        return;
-      }
-
-      // 验证商品属性(如果有SKU)
-      if (formData.sku_prices && formData.sku_prices.length > 0) {
-        // 验证SKU数据
-        for (let i = 0; i < formData.sku_prices.length; i++) {
-          const item = formData.sku_prices[i];
-          if (!item.price || item.price <= 0) {
-            ElMessage.error(`第${i + 1}个规格的销售价格不能为空且必须大于0`);
-            return;
-          }
-          if (!item.otPrice || item.otPrice <= 0) {
-            ElMessage.error(`第${i + 1}个规格的市场价不能为空且必须大于0`);
-            return;
-          }
-          if (item.stock === null || item.stock === undefined || item.stock < 0) {
-            ElMessage.error(`第${i + 1}个规格的商品库存不能为空且不能小于0`);
-            return;
-          }
+    // 验证商品属性(如果有SKU)
+    if (formData.sku_prices && formData.sku_prices.length > 0) {
+      // 验证SKU数据
+      for (let i = 0; i < formData.sku_prices.length; i++) {
+        const item = formData.sku_prices[i];
+        if (!item.price || item.price <= 0) {
+          ElMessage.error(t('modules.goods.specificationSalePriceError', { index: i + 1 }));
+          return;
+        }
+        if (!item.otPrice || item.otPrice <= 0) {
+          ElMessage.error(t('modules.goods.specificationMarketPriceError', { index: i + 1 }));
+          return;
+        }
+        if (item.stock === null || item.stock === undefined || item.stock < 0) {
+          ElMessage.error(t('modules.goods.specificationStockError', { index: i + 1 }));
+          return;
         }
       }
-
-      // 调用统一保存接口
-      const data = { ...formData };
-      await saveGoods(data);
-      // 测试代码
-
-      // 提交成功,跳转到成功页面
-      submitSuccess.value = true;
-      currentStep.value = 2;
-      ElMessage.success('商品保存成功');
-    } catch (error) {
-      submitError.value = error.message || '提交失败';
-      ElMessage.error('提交失败:' + error.message);
-    } finally {
-      submitLoading.value = false;
     }
-  };
 
-  // 生成后端需要的数据格式(使用统一转换工具)
-  const generateAttrValueData = () => {
-    const { attrValue } = dataConverter.convertFrontendToBackend(
-      formData.skus,
-      formData.sku_prices,
-    );
-    return attrValue;
-  };
-
-  const generateAttrData = () => {
-    const { attr } = dataConverter.convertFrontendToBackend(formData.skus, formData.sku_prices);
-    return attr;
-  };
-
-  // 统一提交接口 - 保存商品信息和属性
-  const saveGoods = async (data) => {
-    try {
-      // 准备基本商品数据
-      const submitData = {
-        ...data,
-        // 图片转换为逗号分隔字符串
-        image: convertImagesToString(data.image),
-        sliderImage: convertImagesToString(data.sliderImage),
-        flatPattern: convertImagesToString(data.flatPattern),
-      };
-
-      // 添加商品属性数据
-      if (formData.sku_prices && formData.sku_prices.length > 0) {
-        submitData.attrValue = generateAttrValueData();
-        submitData.attr = generateAttrData();
-      }
+    // 调用统一保存接口
+    const data = { ...formData };
+    await saveGoods(data);
+    // 测试代码
+
+    // 提交成功,跳转到成功页面
+    submitSuccess.value = true;
+    currentStep.value = 2;
+    ElMessage.success(t('modules.goods.goodsSaveSuccess'));
+  } catch (error) {
+    submitError.value = error.message || '提交失败';
+    ElMessage.error('提交失败:' + error.message);
+  } finally {
+    submitLoading.value = false;
+  }
+};
+
+// 生成后端需要的数据格式(使用统一转换工具)
+const generateAttrValueData = () => {
+  const { attrValue } = dataConverter.convertFrontendToBackend(
+    formData.skus,
+    formData.sku_prices,
+  );
+  return attrValue;
+};
+
+const generateAttrData = () => {
+  const { attr } = dataConverter.convertFrontendToBackend(formData.skus, formData.sku_prices);
+  return attr;
+};
+
+// 统一提交接口 - 保存商品信息和属性
+const saveGoods = async (data) => {
+  try {
+    // 准备基本商品数据
+    const submitData = {
+      ...data,
+      // 图片转换为逗号分隔字符串
+      image: convertImagesToString(data.image),
+      sliderImage: convertImagesToString(data.sliderImage),
+      flatPattern: convertImagesToString(data.flatPattern),
+    };
 
-      // 调用配置文件中的接口 - 根据是否有ID判断新增还是编辑
-      const { code } = isEdit.value
-        ? await api.goods.edit(submitData)
-        : await api.goods.add(submitData);
-      if (code === '200') {
-        return true;
-      } else {
-        throw new Error('保存失败');
-      }
-    } catch (error) {
-      console.error('保存商品失败:', error);
-      throw error;
+    // 添加商品属性数据
+    if (formData.sku_prices && formData.sku_prices.length > 0) {
+      submitData.attrValue = generateAttrValueData();
+      submitData.attr = generateAttrData();
     }
-  };
 
-  // 获取分类数据
-  const getCategoryData = async () => {
-    try {
-      const response = await api.category.list({ size: 100 });
-      if (response.code == '200') {
-        // 只使用一级分类
-        categoryOptions.value = response.data.list;
-      }
-    } catch (error) {
-      console.error('获取分类失败:', error);
+    // 调用配置文件中的接口 - 根据是否有ID判断新增还是编辑
+    const { code } = isEdit.value
+      ? await api.goods.edit(submitData)
+      : await api.goods.add(submitData);
+    if (code === '200') {
+      return true;
+    } else {
+      throw new Error('保存失败');
     }
-  };
-
-  // 初始化
-  const init = () => {
-    getCategoryData();
-    if (props.modal?.params?.type === 'edit' && props.modal?.params?.id) {
-      // 编辑模式,加载商品数据
-      loadGoodsData(props.modal.params.id);
+  } catch (error) {
+    console.error('保存商品失败:', error);
+    throw error;
+  }
+};
+
+// 获取分类数据
+const getCategoryData = async () => {
+  try {
+    const response = await api.category.list({ size: 100 });
+    if (response.code == '200') {
+      // 只使用一级分类
+      categoryOptions.value = response.data.list;
     }
-  };
-
-  // 加载商品数据
-  const loadGoodsData = async (id) => {
-    try {
-      const response = await api.goods.detail(id);
-
-      if (response.code == '200') {
-        const data = { ...response.data };
-
-        // 转换图片字段:将逗号分隔的字符串转换为数组
-        data.image = dataConverter.stringToImages(data.image);
-        data.sliderImage = dataConverter.stringToImages(data.sliderImage);
-        data.flatPattern = dataConverter.stringToImages(data.flatPattern);
-
-        // 处理商品属性数据:将后端的 attr 和 attrValue 转换为前端格式
-        if (data.attr && data.attrValue && data.attr.length > 0 && data.attrValue.length > 0) {
-          const { skus, sku_prices } = dataConverter.convertBackendToFrontend(
-            data.attr,
-            data.attrValue,
-          );
-
-          data.skus = skus.length > 0 ? skus : formData.skus;
-          data.sku_prices = sku_prices;
-          data.specType = skus.length > 0 ? 1 : 0;
-        } else {
-          // 如果没有属性数据,保持默认的单规格结构
-          data.skus = formData.skus;
-          data.sku_prices = [];
-          data.specType = 1; // 默认多规格模式
-        }
+  } catch (error) {
+    console.error('获取分类失败:', error);
+  }
+};
+
+// 初始化
+const init = () => {
+  getCategoryData();
+  if (props.modal?.params?.type === 'edit' && props.modal?.params?.id) {
+    // 编辑模式,加载商品数据
+    loadGoodsData(props.modal.params.id);
+  }
+};
+
+// 加载商品数据
+const loadGoodsData = async (id) => {
+  try {
+    const response = await api.goods.detail(id);
+
+    if (response.code == '200') {
+      const data = { ...response.data };
+
+      // 转换图片字段:将逗号分隔的字符串转换为数组
+      data.image = dataConverter.stringToImages(data.image);
+      data.sliderImage = dataConverter.stringToImages(data.sliderImage);
+      data.flatPattern = dataConverter.stringToImages(data.flatPattern);
+
+      // 处理商品属性数据:将后端的 attr 和 attrValue 转换为前端格式
+      if (data.attr && data.attrValue && data.attr.length > 0 && data.attrValue.length > 0) {
+        const { skus, sku_prices } = dataConverter.convertBackendToFrontend(
+          data.attr,
+          data.attrValue,
+        );
+
+        data.skus = skus.length > 0 ? skus : formData.skus;
+        data.sku_prices = sku_prices;
+        data.specType = skus.length > 0 ? 1 : 0;
+      } else {
+        // 如果没有属性数据,保持默认的单规格结构
+        data.skus = formData.skus;
+        data.sku_prices = [];
+        data.specType = 1; // 默认多规格模式
+      }
 
-        // 将处理后的数据填充到表单中 - 使用直接赋值确保响应式更新
-        // 基本信息字段
-        formData.id = data.id;
-        formData.cateId = data.cateId;
-        formData.storeName = data.storeName;
-        formData.keyword = data.keyword;
-        formData.itemBrand = data.itemBrand;
-        formData.storeInfo = data.storeInfo;
-        formData.itemNumber = data.itemNumber;
-        formData.price = data.price;
-        formData.otPrice = data.otPrice;
-        formData.stock = data.stock;
-        formData.stockThreshold = data.stockThreshold;
-        formData.isShow = data.isShow;
-        formData.itemSupplier = data.itemSupplier;
-        formData.sort = data.sort;
-        formData.cost = data.cost;
-        formData.vipPrice = data.vipPrice;
-
-        // 图片字段 - 使用nextTick避免响应式循环
-        await new Promise((resolve) => {
-          setTimeout(() => {
-            formData.image = [...data.image];
-            formData.sliderImage = [...data.sliderImage];
-            formData.flatPattern = [...data.flatPattern];
-            resolve();
-          }, 0);
-        });
+      // 将处理后的数据填充到表单中 - 使用直接赋值确保响应式更新
+      // 基本信息字段
+      formData.id = data.id;
+      formData.cateId = data.cateId;
+      formData.storeName = data.storeName;
+      formData.keyword = data.keyword;
+      formData.itemBrand = data.itemBrand;
+      formData.storeInfo = data.storeInfo;
+      formData.itemNumber = data.itemNumber;
+      formData.price = data.price;
+      formData.otPrice = data.otPrice;
+      formData.stock = data.stock;
+      formData.stockThreshold = data.stockThreshold;
+      formData.isShow = data.isShow;
+      formData.itemSupplier = data.itemSupplier;
+      formData.sort = data.sort;
+      formData.cost = data.cost;
+      formData.vipPrice = data.vipPrice;
+
+      // 图片字段 - 使用nextTick避免响应式循环
+      await new Promise((resolve) => {
+        setTimeout(() => {
+          formData.image = [...data.image];
+          formData.sliderImage = [...data.sliderImage];
+          formData.flatPattern = [...data.flatPattern];
+          resolve();
+        }, 0);
+      });
 
-        // 规格字段 - 使用全新对象替换
-        formData.specType = data.specType;
+      // 规格字段 - 使用全新对象替换
+      formData.specType = data.specType;
 
-        // 重要:先清空数组,然后添加新元素
-        formData.skus.length = 0;
-        data.skus.forEach((item) => formData.skus.push(item));
+      // 重要:先清空数组,然后添加新元素
+      formData.skus.length = 0;
+      data.skus.forEach((item) => formData.skus.push(item));
 
-        formData.sku_prices.length = 0;
-        data.sku_prices.forEach((item) => formData.sku_prices.push(item));
+      formData.sku_prices.length = 0;
+      data.sku_prices.forEach((item) => formData.sku_prices.push(item));
 
-        // 强制触发响应式更新
-        await new Promise((resolve) => {
-          setTimeout(() => {
-            // 强制重新构建SKU表格
-            buildSkuPriceTable();
+      // 强制触发响应式更新
+      await new Promise((resolve) => {
+        setTimeout(() => {
+          // 强制重新构建SKU表格
+          buildSkuPriceTable();
 
-            // 强制重新渲染组件
-            forceUpdateKey.value++;
-            resolve();
-          }, 100);
-        });
-      }
-    } catch (error) {
-      console.error('加载商品数据失败:', error);
+          // 强制重新渲染组件
+          forceUpdateKey.value++;
+          resolve();
+        }, 100);
+      });
     }
-  };
+  } catch (error) {
+    console.error('加载商品数据失败:', error);
+  }
+};
 
-  // 组件挂载时初始化
-  onMounted(() => {
-    init();
-  });
+// 组件挂载时初始化
+onMounted(() => {
+  init();
+});
 </script>
 
 <style lang="scss" scoped>
-  .goods-edit-new {
-    .el-main {
-      padding: 20px;
-      background: #f5f5f5;
-      overflow-y: auto;
-    }
+.goods-edit-new {
+  .el-main {
+    padding: 20px;
+    background: #f5f5f5;
+    overflow-y: auto;
+  }
 
-    .step-progress {
-      background: #fff;
+  .step-progress {
+    background: #fff;
+    padding: 30px;
+    border-radius: 8px;
+    margin-bottom: 20px;
+    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+  }
+
+  .step-content {
+    background: #fff;
+    border-radius: 8px;
+    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+    min-height: 500px;
+
+    .step-panel {
       padding: 30px;
-      border-radius: 8px;
-      margin-bottom: 20px;
-      box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
     }
 
-    .step-content {
-      background: #fff;
-      border-radius: 8px;
-      box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
-      min-height: 500px;
+    .upload-section {
+      .upload-tip {
+        margin-top: 8px;
+        font-size: 12px;
+        color: #999;
+        line-height: 1.4;
+      }
+    }
 
-      .step-panel {
-        padding: 30px;
+    .spec-card {
+      .card-header {
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+        font-weight: 500;
       }
 
-      .upload-section {
-        .upload-tip {
-          margin-top: 8px;
-          font-size: 12px;
-          color: #999;
-          line-height: 1.4;
-        }
+      .single-spec {
+        padding: 20px 0;
       }
 
-      .spec-card {
-        .card-header {
-          display: flex;
-          justify-content: space-between;
-          align-items: center;
-          font-weight: 500;
+      .multi-spec {
+        padding: 20px 0;
+      }
+    }
+
+    .submit-panel {
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      min-height: 500px;
+
+      .submit-success {
+        text-align: center;
+
+        .success-icon {
+          margin-bottom: 20px;
         }
 
-        .single-spec {
-          padding: 20px 0;
+        h3 {
+          margin: 0 0 10px 0;
+          font-size: 20px;
+          color: #303133;
         }
 
-        .multi-spec {
-          padding: 20px 0;
+        p {
+          margin: 0;
+          color: #606266;
+          font-size: 14px;
         }
       }
+    }
+  }
+}
+
+.batch-operations {
+  margin: 16px 0;
+  padding: 16px;
+  background: #f8f9fa;
+  border-radius: 6px;
+  display: flex;
+  align-items: center;
+  flex-wrap: wrap;
+  gap: 10px;
+}
+
+.sku-wrap {
+  width: 100%;
+  border: 1px solid #d9d9d9;
+  padding: 8px;
+  box-sizing: border-box;
+
+  .sku {
+    width: 100%;
+    min-height: 100px;
 
-      .submit-panel {
-        display: flex;
-        align-items: center;
-        justify-content: center;
-        min-height: 500px;
+    .sku-key {
+      width: 100%;
+      height: 40px;
+      color: var(--sa-subtitle);
+      padding: 0 16px;
+      background: var(--sa-table-header-bg);
+      font-size: 14px;
 
-        .submit-success {
-          text-align: center;
+      .sku-key-input {
+        width: 120px;
+      }
 
-          .success-icon {
-            margin-bottom: 20px;
-          }
+      .sku-key-icon {
+        color: var(--el-color-primary);
+      }
+    }
 
-          h3 {
-            margin: 0 0 10px 0;
-            font-size: 20px;
-            color: #303133;
-          }
+    .sku-value {
+      padding: 12px 0 0 30px;
+      font-size: 14px;
+      color: var(--sa-subtitle);
 
-          p {
-            margin: 0;
-            color: #606266;
-            font-size: 14px;
-          }
-        }
+      .sku-value-title {
+        height: 32px;
       }
-    }
-  }
 
-  .batch-operations {
-    margin: 16px 0;
-    padding: 16px;
-    background: #f8f9fa;
-    border-radius: 6px;
-    display: flex;
-    align-items: center;
-    flex-wrap: wrap;
-    gap: 10px;
-  }
+      .sku-value-box {
+        position: relative;
+        margin-right: 24px;
+        border: 1px solid #e4e7ed;
+        border-radius: 6px;
+        padding: 8px;
+        background: #fafafa;
 
-  .sku-wrap {
-    width: 100%;
-    border: 1px solid #d9d9d9;
-    padding: 8px;
-    box-sizing: border-box;
-    .sku {
-      width: 100%;
-      min-height: 100px;
-      .sku-key {
-        width: 100%;
-        height: 40px;
-        color: var(--sa-subtitle);
-        padding: 0 16px;
-        background: var(--sa-table-header-bg);
-        font-size: 14px;
-        .sku-key-input {
-          width: 120px;
-        }
-        .sku-key-icon {
-          color: var(--el-color-primary);
+        .sku-value-content {
+          display: flex;
+          align-items: center;
+          gap: 8px;
         }
-      }
-      .sku-value {
-        padding: 12px 0 0 30px;
-        font-size: 14px;
-        color: var(--sa-subtitle);
-        .sku-value-title {
-          height: 32px;
+
+        .sku-value-input {
+          width: 104px;
         }
-        .sku-value-box {
-          position: relative;
-          margin-right: 24px;
-          border: 1px solid #e4e7ed;
-          border-radius: 6px;
-          padding: 8px;
-          background: #fafafa;
-
-          .sku-value-content {
-            display: flex;
-            align-items: center;
-            gap: 8px;
-          }
 
-          .sku-value-input {
-            width: 104px;
-          }
+        .sku-value-image {
+          flex-shrink: 0;
+        }
 
-          .sku-value-image {
-            flex-shrink: 0;
-          }
+        .sku-value-icon {
+          position: absolute;
+          right: -9px;
+          top: -12px;
+          width: 18px;
+          height: 18px;
+          color: var(--el-color-danger);
+          background: white;
+          border-radius: 50%;
+          box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
+          cursor: pointer;
+          z-index: 10;
+          display: flex;
+          align-items: center;
+          justify-content: center;
 
-          .sku-value-icon {
-            position: absolute;
-            right: -9px;
-            top: -12px;
-            width: 18px;
-            height: 18px;
+          &:hover {
             color: var(--el-color-danger);
-            background: white;
-            border-radius: 50%;
-            box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
-            cursor: pointer;
-            z-index: 10;
-            display: flex;
-            align-items: center;
-            justify-content: center;
-
-            &:hover {
-              color: var(--el-color-danger);
-              background: var(--el-color-danger-light-9);
-            }
+            background: var(--el-color-danger-light-9);
           }
         }
-        .sku-value-add {
-          width: 104px;
-          height: 32px;
-          font-size: 14px;
-          color: var(--el-color-primary);
-        }
       }
-    }
-    .sku-tools {
-      width: 100%;
-      height: 40px;
-      color: #434343;
-      padding-left: 16px;
-      background: var(--sa-table-header-bg);
-      font-size: 12px;
+
+      .sku-value-add {
+        height: 32px;
+        font-size: 14px;
+        color: var(--el-color-primary);
+      }
     }
   }
-  .alledit-input {
-    margin-bottom: 10px;
+
+  .sku-tools {
+    width: 100%;
+    height: 40px;
+    color: #434343;
+    padding-left: 16px;
+    background: var(--sa-table-header-bg);
+    font-size: 12px;
   }
+}
 
-  .sku-table-wrap {
+.alledit-input {
+  margin-bottom: 10px;
+}
+
+.sku-table-wrap {
+  width: 100%;
+  overflow: auto;
+  margin-top: 16px;
+
+  .sku-table {
     width: 100%;
-    overflow: auto;
-    margin-top: 16px;
-    .sku-table {
-      width: 100%;
-      border: 1px solid var(--sa-border);
-      tbody {
-        font-size: 12px;
+    border: 1px solid var(--sa-border);
+
+    tbody {
+      font-size: 12px;
+    }
+
+    th {
+      font-size: 12px;
+      color: var(--subtitle);
+      height: 32px;
+      line-height: 1;
+      padding-left: 12px;
+      box-sizing: border-box;
+      text-align: left;
+
+      .sku-table-header-title {
+        margin-right: 10px;
       }
-      th {
+
+      .th-title {
         font-size: 12px;
         color: var(--subtitle);
-        height: 32px;
-        line-height: 1;
-        padding-left: 12px;
-        box-sizing: border-box;
-        text-align: left;
-        .sku-table-header-title {
-          margin-right: 10px;
-        }
-        .th-title {
-          font-size: 12px;
-          color: var(--subtitle);
-          font-weight: bold;
-        }
-      }
-      td {
-        min-width: 88px;
-        padding: 0 10px;
-        height: 40px;
-        box-sizing: border-box;
-        &.image {
-          min-width: 48px;
-        }
-        &.stock {
-          min-width: 138px;
-        }
-        &.stockThreshold {
-          min-width: 168px;
-          .sku-stock-switch {
-            margin-right: 10px;
-          }
-        }
-        &.sn {
-          min-width: 116px;
-        }
+        font-weight: bold;
       }
     }
-  }
-  .batch-icon {
-    width: 12px;
-    height: 12px;
-    margin-left: 5px;
-    cursor: pointer;
-  }
 
-  // 弹窗中的特殊样式
-  :deep(.sa-dialog) {
-    .el-dialog__body {
-      padding: 0;
-      height: 80vh;
-      overflow: hidden;
-    }
-  }
+    td {
+      min-width: 88px;
+      padding: 0 10px;
+      height: 40px;
+      box-sizing: border-box;
 
-  // 表单样式优化
-  :deep(.el-form-item) {
-    margin-bottom: 24px;
+      &.image {
+        min-width: 48px;
+      }
 
-    .el-form-item__label {
-      font-weight: 500;
-      color: #303133;
-    }
+      &.stock {
+        min-width: 138px;
+      }
 
-    .form-tip {
-      margin-top: 5px;
-      font-size: 12px;
-      color: #909399;
-      line-height: 1.4;
-    }
-  }
+      &.stockThreshold {
+        min-width: 168px;
+
+        .sku-stock-switch {
+          margin-right: 10px;
+        }
+      }
 
-  :deep(.el-input) {
-    .el-input__inner {
-      border-radius: 6px;
+      &.sn {
+        min-width: 116px;
+      }
     }
   }
+}
+
+.batch-icon {
+  width: 12px;
+  height: 12px;
+  margin-left: 5px;
+  cursor: pointer;
+}
+
+// 弹窗中的特殊样式
+:deep(.sa-dialog) {
+  .el-dialog__body {
+    padding: 0;
+    height: 80vh;
+    overflow: hidden;
+  }
+}
 
-  :deep(.el-select) {
-    width: 100%;
+// 表单样式优化
+:deep(.el-form-item) {
+  margin-bottom: 24px;
 
-    .el-input__inner {
-      border-radius: 6px;
-    }
+  .el-form-item__label {
+    font-weight: 500;
+    color: #303133;
   }
 
-  :deep(.el-cascader) {
-    .el-input__inner {
-      border-radius: 6px;
-    }
+  .form-tip {
+    margin-top: 5px;
+    font-size: 12px;
+    color: #909399;
+    line-height: 1.4;
   }
+}
 
-  :deep(.el-textarea) {
-    .el-textarea__inner {
-      border-radius: 6px;
-    }
+:deep(.el-input) {
+  .el-input__inner {
+    border-radius: 6px;
   }
+}
 
-  // 步骤条样式
-  :deep(.el-steps) {
-    .el-step__title {
-      font-size: 16px;
-      font-weight: 500;
-    }
+:deep(.el-select) {
+  width: 100%;
 
-    .el-step__description {
-      font-size: 14px;
-    }
+  .el-input__inner {
+    border-radius: 6px;
   }
+}
 
-  // 卡片样式
-  :deep(.el-card) {
-    border-radius: 8px;
-    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
-
-    .el-card__header {
-      padding: 20px;
-      border-bottom: 1px solid #f0f0f0;
-    }
+:deep(.el-cascader) {
+  .el-input__inner {
+    border-radius: 6px;
+  }
+}
 
-    .el-card__body {
-      padding: 20px;
-    }
+:deep(.el-textarea) {
+  .el-textarea__inner {
+    border-radius: 6px;
   }
+}
 
-  /* 必填项样式 */
-  .required {
-    color: #f56c6c;
-    margin-right: 4px;
+// 步骤条样式
+:deep(.el-steps) {
+  .el-step__title {
+    font-size: 16px;
+    font-weight: 500;
   }
 
-  /* 错误状态样式 */
-  .is-error .el-input__wrapper {
-    border-color: #f56c6c !important;
-    box-shadow: 0 0 0 1px #f56c6c inset !important;
+  .el-step__description {
+    font-size: 14px;
   }
+}
 
-  /* 成功页面样式 */
-  .submit-result {
-    text-align: center;
-    padding: 60px 20px;
+// 卡片样式
+:deep(.el-card) {
+  border-radius: 8px;
+  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
 
-    .success-content,
-    .loading-content {
-      .success-icon,
-      .loading-icon {
-        margin-bottom: 20px;
-      }
+  .el-card__header {
+    padding: 20px;
+    border-bottom: 1px solid #f0f0f0;
+  }
 
-      h3 {
-        font-size: 24px;
-        color: #303133;
-        margin-bottom: 12px;
-      }
+  .el-card__body {
+    padding: 20px;
+  }
+}
+
+/* 必填项样式 */
+.required {
+  color: #f56c6c;
+  margin-right: 4px;
+}
+
+/* 错误状态样式 */
+.is-error .el-input__wrapper {
+  border-color: #f56c6c !important;
+  box-shadow: 0 0 0 1px #f56c6c inset !important;
+}
+
+/* 成功页面样式 */
+.submit-result {
+  text-align: center;
+  padding: 60px 20px;
+
+  .success-content,
+  .loading-content {
+
+    .success-icon,
+    .loading-icon {
+      margin-bottom: 20px;
+    }
 
-      p {
-        font-size: 14px;
-        color: #909399;
-        margin-bottom: 30px;
-      }
+    h3 {
+      font-size: 24px;
+      color: #303133;
+      margin-bottom: 12px;
+    }
 
-      .action-buttons {
-        .el-button {
-          margin: 0 8px;
-        }
-      }
+    p {
+      font-size: 14px;
+      color: #909399;
+      margin-bottom: 30px;
     }
 
-    .loading-content {
-      .loading-icon {
-        animation: rotate 2s linear infinite;
+    .action-buttons {
+      .el-button {
+        margin: 0 8px;
       }
     }
   }
 
-  @keyframes rotate {
-    from {
-      transform: rotate(0deg);
-    }
-    to {
-      transform: rotate(360deg);
+  .loading-content {
+    .loading-icon {
+      animation: rotate 2s linear infinite;
     }
   }
+}
+
+@keyframes rotate {
+  from {
+    transform: rotate(0deg);
+  }
+
+  to {
+    transform: rotate(360deg);
+  }
+}
 </style>

+ 99 - 63
src/app/shop/admin/goods/goods/index.vue

@@ -12,25 +12,34 @@
               @reset="handleReset"
             >
               <template #custom="{ data }">
-                <el-form-item label="价格区间">
+                <el-form-item :label="t('form.priceRange')">
                   <div class="range-input-group">
-                    <el-input v-model="data.minPrice" placeholder="最低价格" clearable />
-                    <span class="range-separator"></span>
-                    <el-input v-model="data.maxPrice" placeholder="最高价格" clearable />
+                    <el-input v-model="data.minPrice" :placeholder="t('form.minPrice')" clearable />
+                    <span class="range-separator">{{ t('common.to') }}</span>
+                    <el-input v-model="data.maxPrice" :placeholder="t('form.maxPrice')" clearable />
                   </div>
                 </el-form-item>
               </template>
             </sa-search-simple>
           </div>
-          <el-tabs class="sa-tabs" v-model="currentStatus" @tab-change="handleTabChange">
-            <el-tab-pane :label="`全部(${statusCounts.all})`" name="all"></el-tab-pane>
-            <el-tab-pane :label="`已上架(${statusCounts.alreadyListed})`" name="up"></el-tab-pane>
-            <el-tab-pane :label="`已下架(${statusCounts.removed})`" name="down"></el-tab-pane>
+          <el-tabs class="sa-tabs" v-model="currentStatus" @tab-click="handleTabClick">
+            <el-tab-pane
+              :label="`${t('common.all')}(${statusCounts.all})`"
+              name="all"
+            ></el-tab-pane>
+            <el-tab-pane
+              :label="`${t('modules.goods.onSale')}(${statusCounts.alreadyListed})`"
+              name="up"
+            ></el-tab-pane>
+            <el-tab-pane
+              :label="`${t('modules.goods.offSale')}(${statusCounts.removed})`"
+              name="down"
+            ></el-tab-pane>
             <!-- <el-tab-pane :label="`已删除(${statusCounts.recycle})`" name="isRecycle"></el-tab-pane> -->
           </el-tabs>
           <div class="sa-title sa-flex sa-row-between">
             <div class="label sa-flex">
-              <span class="left">商品列表</span>
+              <span class="left">{{ t('modules.goods.goodsList') }}</span>
             </div>
             <div>
               <el-button
@@ -38,7 +47,9 @@
                 icon="RefreshRight"
                 @click="getData()"
               ></el-button>
-              <el-button icon="Plus" type="primary" @click="addRow">新建</el-button>
+              <el-button icon="Plus" type="primary" @click="addRow">{{
+                t('common.create')
+              }}</el-button>
             </div>
           </div>
         </el-header>
@@ -61,10 +72,10 @@
               <el-table-column
                 sortable="custom"
                 prop="id"
-                label="商品ID"
+                :label="t('modules.goods.goodsId')"
                 min-width="100"
               ></el-table-column>
-              <el-table-column label="商品信息" min-width="300">
+              <el-table-column :label="t('modules.goods.goodsInfo')" min-width="300">
                 <template #default="scope">
                   <div class="sa-flex">
                     <el-image
@@ -81,61 +92,71 @@
               <el-table-column
                 sortable="custom"
                 prop="price"
-                label="价格"
+                :label="t('form.price')"
                 min-width="150"
                 align="center"
               >
                 <template #default="scope">
-                  <div>原价: ৳{{ scope.row.otPrice }}</div>
-                  <div>在线价: ৳{{ scope.row.price }}</div>
+                  <div>{{ t('modules.goods.marketPrice') }}: ৳{{ scope.row.otPrice }}</div>
+                  <div>{{ t('modules.goods.salePrice') }}: ৳{{ scope.row.price }}</div>
                 </template>
               </el-table-column>
-              <el-table-column label="状态" min-width="100" align="center">
+              <el-table-column :label="t('form.status')" min-width="100" align="center">
                 <template #default="scope">
                   <el-tag :type="statusList.color[scope.row.isShow]">
-                    {{ scope.row.isShow === 1 ? '已上架' : '已下架' }}
+                    {{
+                      scope.row.isShow === 1
+                        ? t('modules.goods.onSale')
+                        : t('modules.goods.offSale')
+                    }}
                   </el-tag>
                 </template>
               </el-table-column>
               <el-table-column
                 prop="itemNumber"
-                label="货号"
+                :label="t('modules.goods.goodsNumber')"
                 min-width="100"
                 align="center"
               ></el-table-column>
               <el-table-column
                 prop="stock"
-                label="库存"
+                :label="t('form.stock')"
                 min-width="100"
                 align="center"
               ></el-table-column>
               <el-table-column
                 sortable="custom"
                 prop="sales"
-                label="销量"
+                :label="t('modules.goods.sales')"
                 min-width="100"
                 align="center"
               ></el-table-column>
-              <el-table-column label="供应商" min-width="120" align="center">
+              <el-table-column
+                :label="t('modules.goods.goodsSupplier')"
+                min-width="120"
+                align="center"
+              >
                 <template #default="scope">
-                  <div>{{ scope.row.itemSupplier || '商家科技有限公司' }}</div>
+                  <div>{{ scope.row.itemSupplier || t('modules.goods.defaultSupplier') }}</div>
                 </template>
               </el-table-column>
-              <el-table-column label="操作" min-width="150" fixed="right">
+              <el-table-column :label="t('common.actions')" min-width="150" fixed="right">
                 <template #default="scope">
                   <div class="sa-flex">
-                    <el-button class="is-link" type="primary" @click="editRow(scope.row)"
-                      >编辑</el-button
-                    >
+                    <el-button class="is-link" type="primary" @click="editRow(scope.row)">
+                      {{ t('common.edit') }}
+                    </el-button>
                     <el-popconfirm
                       width="fit-content"
-                      confirm-button-text="确认"
-                      cancel-button-text="取消"
-                      title="确认删除这条记录?"
+                      :confirm-button-text="t('common.confirm')"
+                      :cancel-button-text="t('common.cancel')"
+                      :title="t('message.confirmDelete')"
                       @confirm="deleteRow(scope.row.id)"
                     >
                       <template #reference>
-                        <el-button class="is-link sa-m-l-12" type="danger"> 删除 </el-button>
+                        <el-button class="is-link sa-m-l-12" type="danger">
+                          {{ t('common.delete') }}
+                        </el-button>
                       </template>
                     </el-popconfirm>
                   </div>
@@ -156,26 +177,26 @@
           ></sa-batch-handle>
         </template>
         <template #right>
-          <sa-pagination :pageData="pageData" @updateFn="getData" />
+          <sa-pagination :pageData="pageData" @updateFn="handlePageChange" />
         </template>
       </sa-view-bar>
     </el-footer>
   </el-container>
 </template>
-<script>
-  export default {
-    name: 'shop.admin.goods.goods',
-  };
-</script>
+
 <script setup>
-  import { onMounted, reactive, ref } from 'vue';
+  import { onMounted, reactive, ref, computed } from 'vue';
   import { api } from '../goods.service';
   import { ElMessageBox, ElMessage } from 'element-plus';
   import { useModal, usePagination } from '@/sheep/hooks';
+  import { useI18n } from 'vue-i18n';
 
   import GoodsEdit from './edit.vue';
   import TabEdit from './tab-edit.vue';
 
+  // 多语言支持
+  const { t } = useI18n();
+
   // getType
   const statusList = reactive({
     data: [],
@@ -204,14 +225,22 @@
   const searchFields = reactive({
     storeName: {
       type: 'input',
-      label: '商品名称',
-      placeholder: '请输入商品名称',
+      get label() {
+        return t('modules.goods.goodsName');
+      },
+      get placeholder() {
+        return t('form.inputName');
+      },
       width: 200,
     },
     cateId: {
       type: 'select',
-      label: '商品分类',
-      placeholder: '请选择商品分类',
+      get label() {
+        return t('modules.goods.goodsCategory');
+      },
+      get placeholder() {
+        return t('form.selectCategory');
+      },
       options: [],
       width: 200,
     },
@@ -244,8 +273,9 @@
     recycle: 0,
   });
 
-  // 标签切换处理
-  const handleTabChange = (status) => {
+  // 标签点击处理(只在用户主动点击时触发)
+  const handleTabClick = (tab) => {
+    const status = tab.props.name;
     currentStatus.value = status;
 
     // 根据不同状态设置不同的筛选参数
@@ -342,7 +372,7 @@
   const { pageData } = usePagination();
 
   // 获取数据
-  async function getData(page, searchParams = {}) {
+  async function getData(page, searchParams = {}, refreshStatusCounts = true) {
     if (page) pageData.page = page;
     loading.value = true;
     const { code, data } = await api.goods.list({
@@ -358,15 +388,22 @@
     }
     loading.value = false;
 
-    // 刷新状态数量
-    getStatusCounts();
+    // 可选择性刷新状态数量
+    if (refreshStatusCounts) {
+      getStatusCounts();
+    }
+  }
+
+  // 分页处理
+  function handlePageChange(page) {
+    getData(page, currentSearchParams.value, false); // 分页时不需要刷新状态数量
   }
 
   // table 字段排序
   function fieldFilter({ prop, order }) {
     table.order = order == 'ascending' ? 'asc' : 'desc';
     table.sort = prop;
-    getData();
+    getData(null, {}, false); // 排序时不需要刷新状态数量
   }
 
   // table 批量选择
@@ -375,26 +412,26 @@
   }
 
   // 批量操作
-  const batchHandleTools = [
+  const batchHandleTools = computed(() => [
     {
       type: 'up',
-      label: '批量上架',
+      label: t('modules.goods.batchOnSale'),
       auth: 'shop.admin.goods.goods.edit',
       class: 'success',
     },
     {
       type: 'down',
-      label: '批量下架',
+      label: t('modules.goods.batchOffSale'),
       auth: 'shop.admin.goods.goods.edit',
       class: 'danger',
     },
     {
       type: 'delete',
-      label: '批量删除',
+      label: t('modules.goods.batchDelete'),
       auth: 'shop.admin.goods.goods.delete',
       class: 'danger',
     },
-  ];
+  ]);
   async function batchHandle(type) {
     let ids = [];
     table.selected.forEach((row) => {
@@ -403,27 +440,27 @@
 
     switch (type) {
       case 'delete':
-        ElMessageBox.confirm('此操作将删除, 是否继续?', '提示', {
-          confirmButtonText: '确定',
-          cancelButtonText: '取消',
+        ElMessageBox.confirm(t('message.confirmBatchDelete'), t('message.tip'), {
+          confirmButtonText: t('common.confirm'),
+          cancelButtonText: t('common.cancel'),
           type: 'warning',
         }).then(() => {
           deleteRow(ids.join(','));
         });
         break;
       case 'up':
-        ElMessageBox.confirm('此操作将批量上架商品, 是否继续?', '提示', {
-          confirmButtonText: '确定',
-          cancelButtonText: '取消',
+        ElMessageBox.confirm(t('message.confirmBatchOnSale'), t('message.tip'), {
+          confirmButtonText: t('common.confirm'),
+          cancelButtonText: t('common.cancel'),
           type: 'warning',
         }).then(() => {
           batchShowStatus(ids.join(','), 1);
         });
         break;
       case 'down':
-        ElMessageBox.confirm('此操作将批量下架商品, 是否继续?', '提示', {
-          confirmButtonText: '确定',
-          cancelButtonText: '取消',
+        ElMessageBox.confirm(t('message.confirmBatchOffSale'), t('message.tip'), {
+          confirmButtonText: t('common.confirm'),
+          cancelButtonText: t('common.cancel'),
           type: 'warning',
         }).then(() => {
           batchShowStatus(ids.join(','), 0);
@@ -544,8 +581,7 @@
 
   onMounted(() => {
     getType();
-    getData();
-    getStatusCounts();
+    getData(); // getData 内部会调用 getStatusCounts(),避免重复调用
   });
 </script>
 <style lang="scss">

+ 29 - 20
src/app/shop/admin/goods/goods/select.vue

@@ -26,8 +26,8 @@
             type="selection"
             width="48"
           ></el-table-column>
-          <el-table-column prop="id" label="商品编号" align="center" />
-          <el-table-column label="商品信息">
+          <el-table-column prop="id" :label="t('modules.goods.goodsNumber')" align="center" />
+          <el-table-column :label="t('modules.goods.goodsInfo')">
             <template #default="scope">
               <div class="sa-flex">
                 <el-image
@@ -41,31 +41,38 @@
               </div>
             </template>
           </el-table-column>
-          <el-table-column label="价格" align="center">
+          <el-table-column :label="t('modules.goods.goodsPrice')" align="center">
             <template #default="scope">
-              <div>原价: ৳{{ scope.row.otPrice }}</div>
-              <div>在线价: ৳{{ scope.row.price }}</div>
+              <div>{{ t('modules.goods.originalPrice') }}: ৳{{ scope.row.otPrice }}</div>
+              <div>{{ t('modules.goods.onlinePrice') }}: ৳{{ scope.row.price }}</div>
             </template>
           </el-table-column>
-          <el-table-column prop="stock" label="库存" min-width="100" align="center">
+          <el-table-column
+            prop="stock"
+            :label="t('modules.goods.goodsStock')"
+            min-width="100"
+            align="center"
+          >
             <template #default="scope">
               {{ scope.row.stock || 0 }}
             </template>
           </el-table-column>
-          <el-table-column v-if="!modal.params.multiple" label="操作" width="80">
+          <el-table-column v-if="!modal.params.multiple" :label="t('common.actions')" width="80">
             <template #default="scope">
               <template v-if="modal.params.ftype == 'score_shop'">
-                <span v-if="scope.row.is_score_shop">已参加</span>
+                <span v-if="scope.row.is_score_shop">{{ t('modules.goods.alreadyJoined') }}</span>
                 <el-button
                   v-if="!scope.row.is_score_shop"
                   link
                   type="primary"
                   @click="singleSelect(scope.row.id)"
-                  >参加</el-button
+                  >{{ t('modules.goods.join') }}</el-button
                 >
               </template>
               <template v-else>
-                <el-button link type="primary" @click="singleSelect(scope.row.id)">选择</el-button>
+                <el-button link type="primary" @click="singleSelect(scope.row.id)">{{
+                  t('common.select')
+                }}</el-button>
               </template>
             </template>
           </el-table-column>
@@ -76,18 +83,14 @@
         :class="modal.params.multiple ? 'sa-row-between' : 'sa-row-right'"
       >
         <sa-pagination :pageData="pageData" layout="total, prev, pager, next" @updateFn="getData" />
-        <el-button v-if="modal.params.multiple" type="primary" @click="confirm">确 定</el-button>
+        <el-button v-if="modal.params.multiple" type="primary" @click="confirm">{{
+          t('common.confirm')
+        }}</el-button>
       </el-footer>
     </el-container>
   </el-container>
 </template>
 
-<script>
-  export default {
-    name: 'GoodsSelect',
-  };
-</script>
-
 <script setup>
   /**@property {Array} ids - 已经选中
    * @param {Boolean} multiple - 是否多选
@@ -98,7 +101,9 @@
   import { api } from '../goods.service';
   import { ElMessage } from 'element-plus';
   import { usePagination } from '@/sheep/hooks';
+  import { useI18n } from 'vue-i18n';
 
+  const { t } = useI18n();
   const { pageData } = usePagination();
 
   const emit = defineEmits(['modalCallBack']);
@@ -108,8 +113,12 @@
   const searchFields = reactive({
     title: {
       type: 'input',
-      label: '商品名称',
-      placeholder: '请输入名称或编号',
+      get label() {
+        return t('modules.goods.goodsName');
+      },
+      get placeholder() {
+        return t('form.inputNameOrNumber');
+      },
       width: 200,
     },
   });
@@ -237,7 +246,7 @@
       }
       ElMessage({
         type: 'warning',
-        message: '已到选择上限',
+        message: t('message.selectionLimitReached'),
       });
       return false;
     }

+ 41 - 22
src/app/shop/admin/order/order/index.vue

@@ -12,15 +12,15 @@
               @reset="handleReset"
             >
               <template #custom="{ data }">
-                <el-form-item label="下单时间">
+                <el-form-item :label="t('modules.order.orderTime')">
                   <el-date-picker
                     v-model="data.createTime"
                     type="datetimerange"
                     value-format="YYYY-MM-DD HH:mm:ss"
                     format="YYYY-MM-DD HH:mm:ss"
-                    range-separator="至"
-                    start-placeholder="开始时间"
-                    end-placeholder="结束时间"
+                    :range-separator="t('common.to')"
+                    :start-placeholder="t('common.startTime')"
+                    :end-placeholder="t('common.endTime')"
                     :editable="false"
                     style="width: 350px"
                   />
@@ -29,19 +29,19 @@
             </sa-search-simple>
           </div>
           <el-tabs class="sa-tabs" v-model="currentStatus" @tab-change="handleTabChange">
-            <el-tab-pane label="全部" name="all"></el-tab-pane>
-            <el-tab-pane label="待支付" name="to_pay"></el-tab-pane>
-            <el-tab-pane label="已支付" name="paid"></el-tab-pane>
-            <el-tab-pane label="已退款" name="refund"></el-tab-pane>
-            <el-tab-pane label="待发货" name="to_ship"></el-tab-pane>
-            <el-tab-pane label="已完成" name="completed"></el-tab-pane>
-            <el-tab-pane label="待收货" name="to_receive"></el-tab-pane>
-            <el-tab-pane label="已关闭" name="to_closed"></el-tab-pane>
-            <el-tab-pane label="已取消" name="cancellation"></el-tab-pane>
+            <el-tab-pane :label="t('common.all')" name="all"></el-tab-pane>
+            <el-tab-pane :label="t('modules.order.pendingPayment')" name="to_pay"></el-tab-pane>
+            <el-tab-pane :label="t('modules.order.paid')" name="paid"></el-tab-pane>
+            <el-tab-pane :label="t('modules.order.refunded')" name="refund"></el-tab-pane>
+            <el-tab-pane :label="t('modules.order.pendingDelivery')" name="to_ship"></el-tab-pane>
+            <el-tab-pane :label="t('modules.order.completed')" name="completed"></el-tab-pane>
+            <el-tab-pane :label="t('modules.order.pendingReceive')" name="to_receive"></el-tab-pane>
+            <el-tab-pane :label="t('modules.order.closed')" name="to_closed"></el-tab-pane>
+            <el-tab-pane :label="t('modules.order.cancelled')" name="cancellation"></el-tab-pane>
           </el-tabs>
           <div class="sa-title sa-flex sa-row-between">
             <div class="label sa-flex">
-              <span class="left">订单列表</span>
+              <span class="left">{{ t('modules.order.orderList') }}</span>
             </div>
             <div>
               <el-button
@@ -221,8 +221,11 @@
   import { onMounted, reactive, ref } from 'vue';
   import { api, getOrderStatusInfo, getPinkStatusInfo } from '../order.service';
   import { useModal, usePagination } from '@/sheep/hooks';
+  import { useI18n } from 'vue-i18n';
   import OrderDispatch from './dispatch.vue';
   import OrderBatchDispatch from './batchDispatch.vue';
+
+  const { t } = useI18n();
   import OrderDetail from './detail.vue';
   import UserEdit from '../../user/list/edit.vue';
   import { ElMessage, ElMessageBox } from 'element-plus';
@@ -231,26 +234,42 @@
   const searchFields = reactive({
     orderId: {
       type: 'input',
-      label: '订单编号',
-      placeholder: '请输入订单编号',
+      get label() {
+        return t('modules.order.orderNo');
+      },
+      get placeholder() {
+        return t('form.inputOrderNo');
+      },
       width: 200,
     },
     realName: {
       type: 'input',
-      label: '用户姓名',
-      placeholder: '请输入用户姓名',
+      get label() {
+        return t('modules.user.userName');
+      },
+      get placeholder() {
+        return t('form.inputUserName');
+      },
       width: 200,
     },
     userPhone: {
       type: 'input',
-      label: '手机号',
-      placeholder: '请输入手机号',
+      get label() {
+        return t('modules.user.userPhone');
+      },
+      get placeholder() {
+        return t('form.inputPhone');
+      },
       width: 200,
     },
     deliveryId: {
       type: 'input',
-      label: '物流单号',
-      placeholder: '请输入物流单号',
+      get label() {
+        return t('modules.order.trackingNumber');
+      },
+      get placeholder() {
+        return t('form.inputTrackingNumber');
+      },
       width: 200,
     },
   });

+ 60 - 31
src/app/shop/admin/user/list/index.vue

@@ -10,13 +10,13 @@
           @reset="handleReset"
         >
           <template #custom="{ data }">
-            <el-form-item label="注册时间">
+            <el-form-item :label="t('modules.user.registrationTime')">
               <el-date-picker
                 v-model="data.dateRange"
                 type="daterange"
-                range-separator="至"
-                start-placeholder="开始日期"
-                end-placeholder="结束日期"
+                :range-separator="t('common.to')"
+                :start-placeholder="t('common.startDate')"
+                :end-placeholder="t('common.endDate')"
                 format="YYYY-MM-DD"
                 value-format="YYYY-MM-DD"
                 style="width: 240px"
@@ -28,7 +28,7 @@
 
       <div class="sa-title sa-flex sa-row-between">
         <div class="label sa-flex">
-          <span class="left">用户列表</span>
+          <span class="left">{{ t('modules.user.userList') }}</span>
         </div>
         <div>
           <el-button class="sa-button-refresh" icon="RefreshRight" @click="getData()"></el-button>
@@ -39,7 +39,7 @@
             :disabled="exportLoading"
             @click="exportUsers"
           >
-            {{ exportLoading ? '导出中...' : '导出数据' }}
+            {{ exportLoading ? t('common.exporting') : t('common.exportData') }}
           </el-button>
         </div>
       </div>
@@ -173,6 +173,9 @@
   import { ElMessageBox, ElMessage } from 'element-plus';
   import { useModal } from '@/sheep/hooks';
   import { usePagination } from '@/sheep/hooks';
+  import { useI18n } from 'vue-i18n';
+
+  const { t } = useI18n();
   import userEdit from './edit.vue';
   import userDetail from './detail.vue';
   import userPassword from './password.vue';
@@ -182,48 +185,74 @@
   const searchFields = reactive({
     name: {
       type: 'input',
-      label: '账号',
-      placeholder: '请输入用户名或昵称',
+      get label() {
+        return t('modules.user.userAccount');
+      },
+      get placeholder() {
+        return t('form.inputUserAccount');
+      },
       width: 200,
     },
     phone: {
       type: 'input',
-      label: '手机号',
-      placeholder: '请输入手机号',
+      get label() {
+        return t('modules.user.userPhone');
+      },
+      get placeholder() {
+        return t('form.inputPhone');
+      },
       width: 150,
     },
     hasLogin: {
       type: 'select',
-      label: '登录权限',
-      placeholder: '请选择登录权限',
+      get label() {
+        return t('modules.user.loginPermission');
+      },
+      get placeholder() {
+        return t('form.selectLoginPermission');
+      },
       width: 120,
-      options: [
-        { label: '全部', value: '' },
-        { label: '允许', value: 1 },
-        { label: '禁止', value: 2 },
-      ],
+      get options() {
+        return [
+          { label: t('common.all'), value: '' },
+          { label: t('common.allow'), value: 1 },
+          { label: t('common.forbid'), value: 2 },
+        ];
+      },
     },
     hasOrder: {
       type: 'select',
-      label: '下单权限',
-      placeholder: '请选择下单权限',
+      get label() {
+        return t('modules.user.orderPermission');
+      },
+      get placeholder() {
+        return t('form.selectOrderPermission');
+      },
       width: 120,
-      options: [
-        { label: '全部', value: '' },
-        { label: '允许', value: 1 },
-        { label: '禁止', value: 2 },
-      ],
+      get options() {
+        return [
+          { label: t('common.all'), value: '' },
+          { label: t('common.allow'), value: 1 },
+          { label: t('common.forbid'), value: 2 },
+        ];
+      },
     },
     hasWithdraw: {
       type: 'select',
-      label: '提现权限',
-      placeholder: '请选择提现权限',
+      get label() {
+        return t('modules.user.withdrawPermission');
+      },
+      get placeholder() {
+        return t('form.selectWithdrawPermission');
+      },
       width: 120,
-      options: [
-        { label: '全部', value: '' },
-        { label: '允许', value: 1 },
-        { label: '禁止', value: 2 },
-      ],
+      get options() {
+        return [
+          { label: t('common.all'), value: '' },
+          { label: t('common.allow'), value: 1 },
+          { label: t('common.forbid'), value: 2 },
+        ];
+      },
     },
   });
 

+ 105 - 0
src/hooks/useFormConfig.js

@@ -0,0 +1,105 @@
+import { computed } from 'vue';
+import { getCurrentLanguage } from '@/locales';
+
+/**
+ * 表单配置hooks
+ * 提供响应式的表单配置,根据当前语言自动调整
+ * @param {Object} options - 配置选项
+ * @param {string} options.zhWidth - 中文标签宽度,默认 '120px'
+ * @param {string} options.enWidth - 英文标签宽度,默认 '160px'
+ * @param {Object} options.customWidths - 自定义宽度配置 { zh: '100px', en: '140px' }
+ * @param {Object} options.spacing - 表单项间距配置 { zh: '20px', en: '24px' }
+ * @param {string} options.defaultButtonSize - 按钮尺寸,默认 'default'
+ * @param {Object} options.fontSize - 字体大小配置 { zh: '14px', en: '13px' }
+ */
+export function useFormConfig(options = {}) {
+  // 解构配置选项,提供默认值
+  const {
+    zhWidth = '120px',
+    enWidth = '160px',
+    customWidths = null,
+    spacing = { zh: '20px', en: '24px' },
+    defaultButtonSize = 'default',
+    fontSize = { zh: '14px', en: '13px' },
+  } = options;
+
+  // 根据当前语言计算表单标签宽度
+  const formLabelWidth = computed(() => {
+    // 如果提供了自定义宽度配置,优先使用
+    if (customWidths) {
+      return getCurrentLanguage() === 'en-US' ? customWidths.en : customWidths.zh;
+    }
+
+    // 使用传入的宽度配置或默认值
+    return getCurrentLanguage() === 'en-US' ? enWidth : zhWidth;
+  });
+
+  // 根据当前语言计算表单项间距
+  const formItemSpacing = computed(() => {
+    return getCurrentLanguage() === 'en-US' ? spacing.en : spacing.zh;
+  });
+
+  // 根据当前语言计算按钮尺寸
+  const buttonSize = computed(() => {
+    return defaultButtonSize;
+  });
+
+  // 根据当前语言计算输入框占位符的样式
+  const inputPlaceholderStyle = computed(() => {
+    return {
+      fontSize: getCurrentLanguage() === 'en-US' ? fontSize.en : fontSize.zh,
+    };
+  });
+
+  // 表单验证消息的样式配置
+  const validationMessageStyle = computed(() => {
+    const currentFontSize = getCurrentLanguage() === 'en-US' ? fontSize.en : fontSize.zh;
+    const baseFontSize = parseInt(currentFontSize);
+    const validationFontSize = `${baseFontSize - 1}px`; // 验证消息字体比正常字体小1px
+
+    return {
+      fontSize: validationFontSize,
+      lineHeight: getCurrentLanguage() === 'en-US' ? '1.4' : '1.3',
+    };
+  });
+
+  // 表单布局配置
+  const formLayout = computed(() => {
+    return {
+      labelWidth: formLabelWidth.value,
+      labelPosition: 'right',
+      size: buttonSize.value,
+    };
+  });
+
+  // 响应式表单配置(适用于不同屏幕尺寸)
+  const responsiveFormConfig = computed(() => {
+    // 获取基础宽度(去掉px单位)
+    const currentWidth = formLabelWidth.value;
+    const baseWidth = parseInt(currentWidth);
+
+    return {
+      xs: `${baseWidth - 20}px`, // 小屏幕
+      sm: `${baseWidth - 10}px`, // 中小屏幕
+      md: `${baseWidth}px`, // 中等屏幕
+      lg: `${baseWidth + 10}px`, // 大屏幕
+      xl: `${baseWidth + 20}px`, // 超大屏幕
+    };
+  });
+
+  return {
+    // 基础配置
+    formLabelWidth,
+    formItemSpacing,
+    buttonSize,
+    inputPlaceholderStyle,
+    validationMessageStyle,
+    formLayout,
+
+    // 响应式配置
+    responsiveFormConfig,
+
+    // 便捷方法
+    getCurrentLanguage,
+  };
+}

+ 217 - 0
src/hooks/useFormConfig.md

@@ -0,0 +1,217 @@
+# useFormConfig Hook 使用文档
+
+## 概述
+
+`useFormConfig` 是一个响应式表单配置 Hook,根据当前语言自动调整表单样式,支持自定义配置。
+
+## 基础用法
+
+### 默认配置
+```javascript
+import { useFormConfig } from '@/hooks/useFormConfig';
+
+const { formLabelWidth, formLayout } = useFormConfig();
+```
+
+**默认值:**
+- 中文标签宽度:`120px`
+- 英文标签宽度:`160px`
+
+### 在模板中使用
+```vue
+<template>
+  <!-- 简单使用 -->
+  <el-form :label-width="formLabelWidth">
+    <!-- 表单项 -->
+  </el-form>
+
+  <!-- 完整配置 -->
+  <el-form v-bind="formLayout">
+    <!-- 表单项 -->
+  </el-form>
+</template>
+```
+
+## 自定义配置
+
+### 1. 自定义标签宽度
+```javascript
+// 方式1:分别设置中英文宽度
+const { formLabelWidth } = useFormConfig({
+  zhWidth: '100px',  // 中文宽度
+  enWidth: '140px'   // 英文宽度
+});
+
+// 方式2:使用自定义宽度对象
+const { formLabelWidth } = useFormConfig({
+  customWidths: {
+    zh: '100px',
+    en: '140px'
+  }
+});
+```
+
+### 2. 自定义间距和字体
+```javascript
+const { formLabelWidth, formItemSpacing, inputPlaceholderStyle } = useFormConfig({
+  zhWidth: '100px',
+  enWidth: '140px',
+  spacing: {
+    zh: '16px',
+    en: '20px'
+  },
+  fontSize: {
+    zh: '13px',
+    en: '12px'
+  }
+});
+```
+
+### 3. 自定义按钮尺寸
+```javascript
+const { buttonSize } = useFormConfig({
+  defaultButtonSize: 'small'  // 'large' | 'default' | 'small'
+});
+```
+
+## 完整配置选项
+
+```javascript
+const options = {
+  // 标签宽度配置
+  zhWidth: '120px',           // 中文标签宽度
+  enWidth: '160px',           // 英文标签宽度
+  customWidths: {             // 自定义宽度对象(优先级最高)
+    zh: '100px',
+    en: '140px'
+  },
+  
+  // 间距配置
+  spacing: {
+    zh: '20px',               // 中文表单项间距
+    en: '24px'                // 英文表单项间距
+  },
+  
+  // 按钮配置
+  defaultButtonSize: 'default', // 按钮尺寸
+  
+  // 字体配置
+  fontSize: {
+    zh: '14px',               // 中文字体大小
+    en: '13px'                // 英文字体大小
+  }
+};
+
+const formConfig = useFormConfig(options);
+```
+
+## 返回值
+
+```javascript
+const {
+  // 基础配置
+  formLabelWidth,           // 响应式标签宽度
+  formItemSpacing,          // 响应式表单项间距
+  buttonSize,               // 按钮尺寸
+  inputPlaceholderStyle,    // 输入框占位符样式
+  validationMessageStyle,   // 验证消息样式
+  formLayout,               // 完整表单布局配置
+  
+  // 响应式配置
+  responsiveFormConfig,     // 响应式宽度配置
+  
+  // 便捷方法
+  getCurrentLanguage,       // 获取当前语言
+} = useFormConfig();
+```
+
+## 使用场景
+
+### 场景1:标准表单(使用默认配置)
+```vue
+<script setup>
+import { useFormConfig } from '@/hooks/useFormConfig';
+
+const { formLabelWidth } = useFormConfig();
+</script>
+
+<template>
+  <el-form :label-width="formLabelWidth">
+    <!-- 表单内容 -->
+  </el-form>
+</template>
+```
+
+### 场景2:紧凑表单(较小的标签宽度)
+```vue
+<script setup>
+import { useFormConfig } from '@/hooks/useFormConfig';
+
+const { formLabelWidth } = useFormConfig({
+  zhWidth: '80px',
+  enWidth: '120px'
+});
+</script>
+```
+
+### 场景3:宽松表单(较大的标签宽度)
+```vue
+<script setup>
+import { useFormConfig } from '@/hooks/useFormConfig';
+
+const { formLabelWidth } = useFormConfig({
+  zhWidth: '150px',
+  enWidth: '200px'
+});
+</script>
+```
+
+### 场景4:完整样式配置
+```vue
+<script setup>
+import { useFormConfig } from '@/hooks/useFormConfig';
+
+const { 
+  formLayout, 
+  buttonSize, 
+  inputPlaceholderStyle,
+  validationMessageStyle 
+} = useFormConfig({
+  zhWidth: '100px',
+  enWidth: '140px',
+  defaultButtonSize: 'small'
+});
+</script>
+
+<template>
+  <el-form v-bind="formLayout">
+    <el-form-item label="名称" prop="name">
+      <el-input 
+        v-model="form.name" 
+        :style="inputPlaceholderStyle"
+      />
+    </el-form-item>
+    
+    <el-form-item>
+      <el-button type="primary" :size="buttonSize">
+        提交
+      </el-button>
+    </el-form-item>
+  </el-form>
+</template>
+
+<style scoped>
+/* 应用验证消息样式 */
+:deep(.el-form-item__error) {
+  font-size: v-bind('validationMessageStyle.fontSize');
+  line-height: v-bind('validationMessageStyle.lineHeight');
+}
+</style>
+```
+
+## 注意事项
+
+1. **优先级**:`customWidths` > `zhWidth/enWidth` > 默认值
+2. **响应式**:所有配置都是响应式的,语言切换时自动更新
+3. **单位**:宽度配置需要包含单位(如 `'120px'`)
+4. **兼容性**:支持所有 Element Plus 表单组件

+ 660 - 0
src/locales/en-US/index.json

@@ -0,0 +1,660 @@
+{
+  "common": {
+    "save": "Save",
+    "update": "Update",
+    "cancel": "Cancel",
+    "confirm": "Confirm",
+    "delete": "Delete",
+    "edit": "Edit",
+    "add": "Add",
+    "create": "Create",
+    "view": "View",
+    "search": "Search",
+    "reset": "Reset",
+    "submit": "Submit",
+    "close": "Close",
+    "back": "Back",
+    "next": "Next",
+    "prev": "Previous",
+    "finish": "Finish",
+    "loading": "Loading...",
+    "all": "All",
+    "actions": "Actions",
+    "expand": "Expand",
+    "collapse": "Collapse",
+    "allow": "Allow",
+    "forbid": "Forbid",
+    "select": "Please select",
+    "previous": "Previous",
+    "uploadImage": "Upload Image",
+    "uploading": "Uploading...",
+    "dragSort": "Drag to Sort",
+    "preview": "Preview",
+    "maxUpload": "Max upload {count} images",
+    "supportFormats": "Support {formats} formats",
+    "maxFileSize": "Max file size {size}MB",
+    "enable": "Enable",
+    "disable": "Disable",
+    "enabled": "Enabled",
+    "disabled": "Disabled",
+    "online": "Online",
+    "offline": "Offline",
+    "active": "Active",
+    "inactive": "Inactive",
+    "success": "Success",
+    "failed": "Failed",
+    "pending": "Pending",
+    "processing": "Processing",
+    "completed": "Completed",
+    "required": "Required",
+    "optional": "Optional",
+    "placeholder": "Please enter",
+    "upload": "Upload",
+    "download": "Download",
+    "saveSuccess": "Save successful",
+    "saveFailed": "Save failed",
+    "deleteSuccess": "Delete successful",
+    "deleteFailed": "Delete failed",
+    "updateSuccess": "Update successful",
+    "updateFailed": "Update failed",
+    "createSuccess": "Create successful",
+    "createFailed": "Create failed",
+    "confirmDelete": "Are you sure you want to delete?",
+    "confirmSave": "Are you sure you want to save?",
+    "confirmSubmit": "Are you sure you want to submit?",
+    "noData": "No data",
+    "total": "Total",
+    "items": "items",
+    "page": "page",
+    "today": "Today",
+    "yesterday": "Yesterday",
+    "thisWeek": "This week",
+    "thisMonth": "This month",
+    "startTime": "Start Time",
+    "endTime": "End Time",
+    "startDate": "Start Date",
+    "endDate": "End Date",
+    "exportData": "Export Data",
+    "exporting": "Exporting...",
+    "to": "to",
+    "and": "and",
+    "or": "or",
+    "language": "Language",
+    "chinese": "中文",
+    "english": "English"
+  },
+  "menu": {
+    "dashboard": "Dashboard",
+    "goods": "Goods Management",
+    "order": "Order Management",
+    "user": "User Management",
+    "system": "System Management",
+    "settings": "Settings",
+    "goodsList": "Goods List",
+    "goodsAdd": "Add Goods",
+    "goodsEdit": "Edit Goods",
+    "goodsCategory": "Goods Category",
+    "goodsBrand": "Goods Brand",
+    "orderList": "Order List",
+    "orderDetail": "Order Detail",
+    "orderRefund": "Refund Management",
+    "userList": "User List",
+    "userRole": "Role Management",
+    "userPermission": "Permission Management",
+    "systemConfig": "System Configuration",
+    "systemLog": "System Log",
+    "systemBackup": "Data Backup",
+    "profile": "Profile",
+    "account": "Account Settings",
+    "security": "Security Settings"
+  },
+  "form": {
+    "name": "Name",
+    "title": "Title",
+    "description": "Description",
+    "content": "Content",
+    "category": "Category",
+    "status": "Status",
+    "type": "Type",
+    "price": "Price",
+    "stock": "Stock",
+    "image": "Image",
+    "sort": "Sort",
+    "createTime": "Create Time",
+    "updateTime": "Update Time",
+    "goodsName": "Goods Name",
+    "goodsPrice": "Goods Price",
+    "goodsStock": "Goods Stock",
+    "goodsCategory": "Goods Category",
+    "goodsBrand": "Goods Brand",
+    "goodsImage": "Goods Image",
+    "goodsDescription": "Goods Description",
+    "goodsStatus": "Goods Status",
+    "marketPrice": "Market Price",
+    "salePrice": "Sale Price",
+    "costPrice": "Cost Price",
+    "orderNo": "Order No.",
+    "orderStatus": "Order Status",
+    "orderAmount": "Order Amount",
+    "orderTime": "Order Time",
+    "payTime": "Pay Time",
+    "deliveryTime": "Delivery Time",
+    "username": "Username",
+    "password": "Password",
+    "email": "Email",
+    "phone": "Phone",
+    "avatar": "Avatar",
+    "role": "Role",
+    "permission": "Permission",
+    "required": "This field is required",
+    "invalidEmail": "Please enter a valid email address",
+    "invalidPhone": "Please enter a valid phone number",
+    "passwordTooShort": "Password must be at least 6 characters",
+    "confirmPassword": "Confirm Password",
+    "passwordNotMatch": "Passwords do not match",
+    "priceRange": "Price Range",
+    "minPrice": "Min Price",
+    "maxPrice": "Max Price",
+    "pleaseInput": "Please enter",
+    "pleaseSelect": "Please select",
+    "pleaseUpload": "Please upload",
+    "inputName": "Please enter goods name",
+    "inputTitle": "Please enter title",
+    "inputDescription": "Please enter description",
+    "selectCategory": "Please select category",
+    "selectStatus": "Please select status",
+    "uploadImage": "Please upload image",
+    "inputOrderNo": "Please input order number",
+    "inputUserName": "Please input user name",
+    "inputUserAccount": "Please input username or nickname",
+    "inputPhone": "Please input phone number",
+    "inputTrackingNumber": "Please input tracking number",
+    "selectLoginPermission": "Please select login permission",
+    "selectOrderPermission": "Please select order permission",
+    "selectWithdrawPermission": "Please select withdraw permission",
+    "inputCategoryName": "Please input category name",
+    "inputSort": "Please input sort order",
+    "inputGoodsName": "Please input goods name (max 100 characters)",
+    "inputSubtitle": "Please input subtitle (max 50 characters)",
+    "categoryNameRequired": "Please input category name",
+    "inputGoodsBrand": "Please input goods brand",
+    "inputGoodsDescription": "Please input goods description (max 500 characters)",
+    "inputGoodsNumber": "Please input goods number",
+    "inputGoodsSupplier": "Please input goods supplier",
+    "selectShippingTemplate": "Please select shipping template",
+    "uploadMainImage": "Upload main image",
+    "uploadCarouselImages": "Upload carousel images",
+    "uploadDetailImages": "Upload detail images",
+    "inputSpecificationName": "Please input specification name",
+    "inputSpecificationValue": "Please input specification value",
+    "selectSpecification": "Please select specification",
+    "inputNameOrNumber": "Please input name or number",
+    "selectSalePrice": "Please select sale price(৳)",
+    "selectMarketPrice": "Please select market price(৳)",
+    "selectPrice": "Select price",
+    "inputStock": "Please input stock",
+    "inputStockThreshold": "Please input stock threshold",
+    "inputSkuCode": "Please input SKU code",
+    "categoryRequired": "Please select goods category",
+    "goodsNameRequired": "Please input goods name",
+    "goodsBrandRequired": "Please input goods brand",
+    "shippingTemplateRequired": "Please select shipping template",
+    "goodsNumberRequired": "Please input goods number",
+    "salePriceRequired": "Please input sale price",
+    "stockRequired": "Please input stock",
+    "goodsStatusRequired": "Please select goods status",
+    "goodsSupplierRequired": "Please input goods supplier",
+    "mainImageRequired": "Please upload main image",
+    "carouselImagesRequired": "Please upload carousel images",
+    "detailImagesRequired": "Please upload detail images"
+  },
+  "message": {
+    "loginSuccess": "Login successful",
+    "logoutSuccess": "Logout successful",
+    "saveSuccess": "Save successful",
+    "deleteSuccess": "Delete successful",
+    "updateSuccess": "Update successful",
+    "createSuccess": "Create successful",
+    "uploadSuccess": "Successfully uploaded {count} images",
+    "loginFailed": "Login failed",
+    "saveFailed": "Save failed",
+    "deleteFailed": "Delete failed",
+    "updateFailed": "Update failed",
+    "createFailed": "Create failed",
+    "uploadFailed": "{count} images failed to upload",
+    "networkError": "Network error",
+    "serverError": "Server error",
+    "unsavedChanges": "You have unsaved changes",
+    "confirmLeave": "Are you sure you want to leave?",
+    "dataWillBeLost": "Data will be lost",
+    "fieldRequired": "This field is required",
+    "invalidFormat": "Invalid format",
+    "valueTooLong": "Value is too long",
+    "valueTooShort": "Value is too short",
+    "confirmDelete": "Are you sure you want to delete this record?",
+    "confirmBatchDelete": "Are you sure you want to delete selected records?",
+    "confirmSave": "Are you sure you want to save?",
+    "confirmSubmit": "Are you sure you want to submit?",
+    "confirmReset": "Are you sure you want to reset?",
+    "confirmBatchOnSale": "This operation will batch put goods on sale, continue?",
+    "confirmBatchOffSale": "This operation will batch take goods off sale, continue?",
+    "tip": "Tip",
+    "selectionLimitReached": "Selection limit reached",
+    "maxUploadLimit": "Maximum {count} images can be uploaded",
+    "uploadError": "Upload failed",
+    "unsupportedFormat": "{extension} format is not supported, please select {formats} format images",
+    "fileSizeExceeded": "Image size cannot exceed {size}MB",
+    "loading": "Loading...",
+    "saving": "Saving...",
+    "deleting": "Deleting...",
+    "uploading": "Uploading...",
+    "processing": "Processing...",
+    "noData": "No data",
+    "noSearchResult": "No search results found",
+    "loadMore": "Load more",
+    "noMore": "No more data"
+  },
+  "error": {
+    "pageNotFound": "Page Not Found",
+    "accessDenied": "Access Denied",
+    "unauthorized": "Unauthorized Access",
+    "serverError": "Server Error",
+    "networkError": "Network Error",
+    "pageNotFoundDesc": "Sorry, the page you are looking for does not exist",
+    "accessDeniedDesc": "Sorry, you do not have permission to access this page",
+    "unauthorizedDesc": "Sorry, you need to login to access this page",
+    "serverErrorDesc": "Sorry, server error occurred",
+    "networkErrorDesc": "Sorry, network connection failed",
+    "goBack": "Go Back",
+    "goHome": "Go Home",
+    "retry": "Retry",
+    "login": "Login",
+    "autoCloseIn": "seconds to auto close",
+    "error401": "401 Unauthorized",
+    "error403": "403 Forbidden",
+    "error404": "404 Not Found",
+    "error500": "500 Server Error"
+  },
+  "modules": {
+    "auth": {
+      "login": "Login",
+      "logout": "Logout",
+      "username": "Username",
+      "account": "Account",
+      "password": "Password",
+      "confirmPassword": "Confirm Password",
+      "rememberMe": "Remember Me",
+      "forgotPassword": "Forgot Password",
+      "loginSuccess": "Login successful",
+      "loginFailed": "Login failed",
+      "logoutSuccess": "Logout successful",
+      "welcomeTo": "Welcome to {appName}",
+      "niceToSeeYou": "Nice to see you again",
+      "userManagement": "User Management",
+      "adminList": "Admin List",
+      "addAdmin": "Add Admin",
+      "editAdmin": "Edit Admin",
+      "deleteAdmin": "Delete Admin",
+      "adminName": "Admin Name",
+      "adminAccount": "Admin Account",
+      "adminRole": "Admin Role",
+      "adminStatus": "Admin Status",
+      "lastLoginTime": "Last Login Time",
+      "roleManagement": "Role Management",
+      "roleList": "Role List",
+      "addRole": "Add Role",
+      "editRole": "Edit Role",
+      "deleteRole": "Delete Role",
+      "roleName": "Role Name",
+      "roleDescription": "Role Description",
+      "rolePermissions": "Role Permissions",
+      "permissionManagement": "Permission Management",
+      "permissionList": "Permission List",
+      "permissionName": "Permission Name",
+      "permissionCode": "Permission Code",
+      "permissionType": "Permission Type",
+      "menuPermission": "Menu Permission",
+      "buttonPermission": "Button Permission",
+      "dataPermission": "Data Permission",
+      "changePassword": "Change Password",
+      "oldPassword": "Old Password",
+      "newPassword": "New Password",
+      "passwordChanged": "Password changed successfully",
+      "passwordNotMatch": "Passwords do not match",
+      "passwordTooShort": "Password must be at least 6 characters",
+      "usernameRequired": "Please enter username",
+      "accountRequired": "Please enter account",
+      "passwordRequired": "Please enter password",
+      "confirmPasswordRequired": "Please confirm password",
+      "roleRequired": "Please select role",
+      "normal": "Normal",
+      "disabled": "Disabled",
+      "locked": "Locked"
+    },
+    "dashboard": {
+      "dashboard": "Dashboard",
+      "overview": "Overview",
+      "statistics": "Statistics",
+      "todayData": "Today Data",
+      "yesterdayData": "Yesterday Data",
+      "thisWeekData": "This Week Data",
+      "thisMonthData": "This Month Data",
+      "salesData": "Sales Data",
+      "salesAmount": "Sales Amount",
+      "orderCount": "Order Count",
+      "userCount": "User Count",
+      "goodsCount": "Goods Count",
+      "salesTrend": "Sales Trend",
+      "orderTrend": "Order Trend",
+      "userTrend": "User Trend",
+      "topGoods": "Top Goods",
+      "topCategories": "Top Categories",
+      "topUsers": "Active Users",
+      "pendingTasks": "Pending Tasks",
+      "pendingOrders": "Pending Orders",
+      "pendingRefunds": "Pending Refunds",
+      "lowStockGoods": "Low Stock Goods",
+      "quickActions": "Quick Actions",
+      "addGoods": "Add Goods",
+      "processOrder": "Process Order",
+      "viewReport": "View Report",
+      "systemSettings": "System Settings",
+      "today": "Today",
+      "yesterday": "Yesterday",
+      "last7Days": "Last 7 Days",
+      "last30Days": "Last 30 Days",
+      "thisMonth": "This Month",
+      "lastMonth": "Last Month",
+      "yuan": "Yuan",
+      "orders": "Orders",
+      "users": "Users",
+      "pieces": "Pieces",
+      "percent": "%",
+      "growthRate": "Growth Rate",
+      "increase": "Increase",
+      "decrease": "Decrease",
+      "noChange": "No Change"
+    },
+    "goods": {
+      "goodsList": "Goods List",
+      "goodsId": "Goods ID",
+      "basicInfo": "Basic Information",
+      "goodsInfo": "Goods Information",
+      "goodsAttributes": "Goods Attributes",
+      "goodsName": "Goods Name",
+      "goodsSubtitle": "Subtitle",
+      "subtitle": "Subtitle",
+      "goodsBrand": "Goods Brand",
+      "originalPrice": "Original Price",
+      "onlinePrice": "Online Price",
+      "alreadyJoined": "Already Joined",
+      "join": "Join",
+      "goodsCategory": "Goods Category",
+      "categoryName": "Category Name",
+      "addCategory": "Add Category",
+      "editCategory": "Edit Category",
+      "goodsDescription": "Goods Description",
+      "goodsNumber": "Goods Number",
+      "goodsSupplier": "Goods Supplier",
+      "defaultSupplier": "Merchant Technology Co., Ltd.",
+      "sales": "Sales",
+      "shippingTemplate": "Shipping Template",
+      "freeShipping": "Free Shipping",
+      "mainImage": "Main Image",
+      "carouselImages": "Carousel Images",
+      "detailImages": "Detail Images",
+      "autoGenerateNumberTip": "If you do not enter a goods number, the system will automatically generate a unique number",
+      "newGoodsOffSaleTip": "New goods are off sale by default, can be modified after saving",
+      "priceStock": "Price & Stock",
+      "salePrice": "Sale Price",
+      "marketPrice": "Market Price",
+      "costPrice": "Cost Price",
+      "vipPrice": "VIP Price",
+      "stock": "Stock",
+      "goodsStock": "Stock",
+      "stockThreshold": "Stock Threshold",
+      "skuCode": "SKU Code",
+      "image": "Image",
+      "goodsStatus": "Goods Status",
+      "onSale": "On Sale",
+      "offSale": "Off Sale",
+      "inStock": "In Stock",
+      "outOfStock": "Out of Stock",
+      "batchOnSale": "Batch On Sale",
+      "batchOffSale": "Batch Off Sale",
+      "batchDelete": "Batch Delete",
+      "specificationSettings": "Goods Specification Settings",
+      "specificationName": "Specification Name",
+      "specificationValue": "Specification Value",
+      "addSpecification": "Add Specification",
+      "addSpecificationValue": "Add Specification Value",
+      "batchSettings": "Batch Settings",
+      "batchSettingsTip": "Unselected specifications default to select all for batch settings",
+      "goodsAddedSuccess": "Goods added successfully, please wait for listing",
+      "continueAdd": "Continue Adding",
+      "savingGoods": "Saving goods...",
+      "pleaseWait": "Please wait, the system is processing your request",
+      "specificationValueExists": "Specification value already exists",
+      "batchSettingsSuccess": "Batch settings successful",
+      "pleaseAddSpecification": "Please add goods specification first",
+      "specificationSalePriceError": "The sale price of specification {index} cannot be empty and must be greater than 0",
+      "specificationMarketPriceError": "The market price of specification {index} cannot be empty and must be greater than 0",
+      "specificationStockError": "The stock of specification {index} cannot be empty and cannot be less than 0",
+      "pleaseCompleteBasicInfo": "Please complete basic goods information",
+      "goodsSaveSuccess": "Goods saved successfully",
+      "sliderImage": "Slider Image",
+      "detailImage": "Detail Image",
+      "whiteBackgroundImage": "White Background Image",
+      "uploadMainImage": "Upload Main Image",
+      "uploadSliderImage": "Upload Slider Image",
+      "uploadDetailImage": "Upload Detail Image",
+      "specification": "Specification",
+      "specType": "Specification Type",
+      "singleSpec": "Single Specification",
+      "multiSpec": "Multiple Specifications",
+      "specName": "Specification Name",
+      "specValue": "Specification Value",
+      "addSpec": "Add Specification",
+      "addSpecValue": "Add Specification Value",
+      "batchSetting": "Batch Setting",
+      "skuPrice": "SKU Price",
+      "skuStock": "SKU Stock",
+      "skuImage": "SKU Image",
+      "addGoods": "Add Goods",
+      "editGoods": "Edit Goods",
+      "deleteGoods": "Delete Goods",
+      "goodsAddSuccess": "Goods added successfully",
+      "goodsUpdateSuccess": "Goods updated successfully",
+      "goodsDeleteSuccess": "Goods deleted successfully",
+      "pleaseSelectGoods": "Please select goods",
+      "confirmDeleteGoods": "Are you sure you want to delete selected goods?",
+      "goodsNameRequired": "Please enter goods name",
+      "goodsBrandRequired": "Please enter goods brand",
+      "goodsCategoryRequired": "Please select goods category",
+      "goodsPriceRequired": "Please enter goods price",
+      "goodsStockRequired": "Please enter goods stock",
+      "goodsSupplierRequired": "Please enter goods supplier",
+      "goodsImageRequired": "Please upload goods image",
+      "goodsDetailImageRequired": "Please upload goods detail image"
+    },
+    "order": {
+      "orderManagement": "Order Management",
+      "orderList": "Order List",
+      "orderDetail": "Order Detail",
+      "orderNo": "Order No.",
+      "orderStatus": "Order Status",
+      "orderAmount": "Order Amount",
+      "orderTime": "Order Time",
+      "payTime": "Pay Time",
+      "deliveryTime": "Delivery Time",
+      "completeTime": "Complete Time",
+      "pendingPayment": "Pending Payment",
+      "paid": "Paid",
+      "pendingDelivery": "Pending Delivery",
+      "pendingReceive": "Pending Receive",
+      "completed": "Completed",
+      "cancelled": "Cancelled",
+      "refunded": "Refunded",
+      "closed": "Closed",
+      "buyerInfo": "Buyer Information",
+      "buyerName": "Buyer Name",
+      "buyerPhone": "Buyer Phone",
+      "buyerAddress": "Delivery Address",
+      "goodsInfo": "Goods Information",
+      "goodsName": "Goods Name",
+      "goodsPrice": "Goods Price",
+      "goodsQuantity": "Quantity",
+      "goodsTotal": "Subtotal",
+      "subtotal": "Subtotal",
+      "shippingFee": "Shipping Fee",
+      "discount": "Discount",
+      "totalAmount": "Total Amount",
+      "actualPayment": "Actual Payment",
+      "logistics": "Logistics",
+      "trackingNumber": "Tracking Number",
+      "courier": "Courier",
+      "shippingAddress": "Shipping Address",
+      "receivingAddress": "Receiving Address",
+      "aftersale": "Aftersale",
+      "refund": "Refund",
+      "return": "Return",
+      "exchange": "Exchange",
+      "refundAmount": "Refund Amount",
+      "refundReason": "Refund Reason",
+      "refundStatus": "Refund Status",
+      "invoice": "Invoice",
+      "invoiceType": "Invoice Type",
+      "invoiceTitle": "Invoice Title",
+      "invoiceContent": "Invoice Content",
+      "processOrder": "Process Order",
+      "shipOrder": "Ship Order",
+      "cancelOrder": "Cancel Order",
+      "refundOrder": "Refund Order",
+      "viewDetail": "View Detail",
+      "printOrder": "Print Order",
+      "exportOrder": "Export Order"
+    },
+    "system": {
+      "systemManagement": "System Management",
+      "systemSettings": "System Settings",
+      "systemConfig": "System Configuration",
+      "basicSettings": "Basic Settings",
+      "siteName": "Site Name",
+      "siteDescription": "Site Description",
+      "siteKeywords": "Site Keywords",
+      "siteLogo": "Site Logo",
+      "siteIcon": "Site Icon",
+      "bannerManagement": "Banner Management",
+      "bannerList": "Banner List",
+      "addBanner": "Add Banner",
+      "editBanner": "Edit Banner",
+      "deleteBanner": "Delete Banner",
+      "bannerTitle": "Banner Title",
+      "bannerImage": "Banner Image",
+      "bannerLink": "Banner Link",
+      "bannerSort": "Sort Order",
+      "paymentConfig": "Payment Configuration",
+      "paymentMethod": "Payment Method",
+      "alipay": "Alipay",
+      "wechatPay": "WeChat Pay",
+      "bankCard": "Bank Card",
+      "paymentStatus": "Payment Status",
+      "notificationManagement": "Notification Management",
+      "systemNotification": "System Notification",
+      "userNotification": "User Notification",
+      "notificationTitle": "Notification Title",
+      "notificationContent": "Notification Content",
+      "notificationType": "Notification Type",
+      "smsManagement": "SMS Management",
+      "smsTemplate": "SMS Template",
+      "smsRecord": "SMS Record",
+      "smsContent": "SMS Content",
+      "smsStatus": "SMS Status",
+      "dataReport": "Data Report",
+      "salesReport": "Sales Report",
+      "userReport": "User Report",
+      "goodsReport": "Goods Report",
+      "systemLog": "System Log",
+      "operationLog": "Operation Log",
+      "loginLog": "Login Log",
+      "errorLog": "Error Log",
+      "systemMonitor": "System Monitor",
+      "serverStatus": "Server Status",
+      "databaseStatus": "Database Status",
+      "cacheStatus": "Cache Status",
+      "backupRestore": "Backup & Restore",
+      "dataBackup": "Data Backup",
+      "dataRestore": "Data Restore",
+      "backupTime": "Backup Time",
+      "backupSize": "Backup Size",
+      "versionInfo": "Version Information",
+      "currentVersion": "Current Version",
+      "updateTime": "Update Time",
+      "enable": "Enable",
+      "disable": "Disable",
+      "configure": "Configure",
+      "test": "Test",
+      "backup": "Backup",
+      "restore": "Restore"
+    },
+    "user": {
+      "userManagement": "User Management",
+      "userList": "User List",
+      "userDetail": "User Detail",
+      "addUser": "Add User",
+      "editUser": "Edit User",
+      "deleteUser": "Delete User",
+      "userId": "User ID",
+      "userName": "User Name",
+      "userAccount": "User Account",
+      "userNickname": "Nickname",
+      "userPhone": "Phone Number",
+      "userEmail": "Email Address",
+      "userAvatar": "Avatar",
+      "userGender": "Gender",
+      "userBirthday": "Birthday",
+      "userAddress": "Address",
+      "male": "Male",
+      "female": "Female",
+      "unknown": "Unknown",
+      "userStatus": "User Status",
+      "active": "Active",
+      "inactive": "Inactive",
+      "frozen": "Frozen",
+      "userLevel": "User Level",
+      "levelManagement": "Level Management",
+      "levelName": "Level Name",
+      "levelDiscount": "Level Discount",
+      "levelCondition": "Upgrade Condition",
+      "userTag": "User Tag",
+      "tagManagement": "Tag Management",
+      "tagName": "Tag Name",
+      "tagColor": "Tag Color",
+      "tagDescription": "Tag Description",
+      "totalUsers": "Total Users",
+      "activeUsers": "Active Users",
+      "newUsers": "New Users",
+      "registrationTime": "Registration Time",
+      "lastLoginTime": "Last Login",
+      "loginCount": "Login Count",
+      "userBehavior": "User Behavior",
+      "orderCount": "Order Count",
+      "totalConsumption": "Total Consumption",
+      "averageOrderValue": "Average Order Value",
+      "loginPermission": "Login Permission",
+      "orderPermission": "Order Permission",
+      "withdrawPermission": "Withdraw Permission",
+      "viewProfile": "View Profile",
+      "editProfile": "Edit Profile",
+      "resetPassword": "Reset Password",
+      "enableUser": "Enable User",
+      "disableUser": "Disable User",
+      "freezeUser": "Freeze User",
+      "phoneRequired": "Please enter phone number",
+      "emailRequired": "Please enter email address",
+      "nameRequired": "Please enter user name",
+      "invalidPhone": "Invalid phone number format",
+      "invalidEmail": "Invalid email format"
+    }
+  }
+}

+ 40 - 0
src/locales/index.js

@@ -0,0 +1,40 @@
+import { createI18n } from 'vue-i18n';
+import zhCN from './zh-CN/index.json';
+import enUS from './en-US/index.json';
+
+const messages = {
+  'zh-CN': zhCN,
+  'en-US': enUS,
+};
+
+const defaultLocale = localStorage.getItem('locale') || 'zh-CN';
+
+const i18n = createI18n({
+  legacy: false,
+  locale: defaultLocale,
+  fallbackLocale: 'zh-CN',
+  messages,
+  globalInjection: true,
+});
+
+// 初始化时设置 data-locale 属性
+if (typeof document !== 'undefined') {
+  document.querySelector('html')?.setAttribute('lang', defaultLocale);
+  document.querySelector('html')?.setAttribute('data-locale', defaultLocale);
+}
+
+export default i18n;
+
+// 导出语言切换函数
+export function setLanguage(locale) {
+  i18n.global.locale.value = locale;
+  localStorage.setItem('locale', locale);
+  document.querySelector('html').setAttribute('lang', locale);
+  // 设置 data-locale 属性用于样式适配
+  document.querySelector('html').setAttribute('data-locale', locale);
+}
+
+// 导出当前语言
+export function getCurrentLanguage() {
+  return i18n.global.locale.value;
+}

+ 664 - 0
src/locales/zh-CN/index.json

@@ -0,0 +1,664 @@
+{
+  "common": {
+    "save": "保存",
+    "update": "更新",
+    "cancel": "取消",
+    "confirm": "确定",
+    "delete": "删除",
+    "edit": "编辑",
+    "add": "新增",
+    "create": "创建",
+    "view": "查看",
+    "search": "搜索",
+    "reset": "重置",
+    "submit": "提交",
+    "close": "关闭",
+    "back": "返回",
+    "next": "下一步",
+    "prev": "上一步",
+    "finish": "完成",
+    "loading": "加载中...",
+    "all": "全部",
+    "actions": "操作",
+    "expand": "展开",
+    "collapse": "收起",
+    "allow": "允许",
+    "forbid": "禁止",
+    "select": "请选择",
+    "previous": "上一步",
+    "uploadImage": "上传图片",
+    "uploading": "上传中...",
+    "dragSort": "拖拽排序",
+    "preview": "预览",
+    "maxUpload": "最多上传{count}张",
+    "supportFormats": "支持{formats}格式",
+    "maxFileSize": "单张图片不超过{size}MB",
+    "enable": "启用",
+    "disable": "禁用",
+    "enabled": "已启用",
+    "disabled": "已禁用",
+    "online": "上架",
+    "offline": "下架",
+    "active": "激活",
+    "inactive": "未激活",
+    "success": "成功",
+    "failed": "失败",
+    "pending": "待处理",
+    "processing": "处理中",
+    "completed": "已完成",
+    "required": "必填",
+    "optional": "可选",
+    "placeholder": "请输入",
+    "upload": "上传",
+    "download": "下载",
+    "saveSuccess": "保存成功",
+    "saveFailed": "保存失败",
+    "deleteSuccess": "删除成功",
+    "deleteFailed": "删除失败",
+    "updateSuccess": "更新成功",
+    "updateFailed": "更新失败",
+    "createSuccess": "创建成功",
+    "createFailed": "创建失败",
+    "confirmDelete": "确定要删除吗?",
+    "confirmSave": "确定要保存吗?",
+    "confirmSubmit": "确定要提交吗?",
+    "noData": "暂无数据",
+    "total": "共",
+    "items": "条",
+    "page": "页",
+    "today": "今天",
+    "yesterday": "昨天",
+    "thisWeek": "本周",
+    "thisMonth": "本月",
+    "startTime": "开始时间",
+    "endTime": "结束时间",
+    "startDate": "开始日期",
+    "endDate": "结束日期",
+    "exportData": "导出数据",
+    "exporting": "导出中...",
+    "to": "至",
+    "and": "和",
+    "or": "或",
+    "language": "语言",
+    "chinese": "中文",
+    "english": "English"
+  },
+  "menu": {
+    "dashboard": "仪表盘",
+    "goods": "商品管理",
+    "order": "订单管理",
+    "user": "用户管理",
+    "system": "系统管理",
+    "settings": "设置",
+    "goodsList": "商品列表",
+    "goodsAdd": "添加商品",
+    "goodsEdit": "编辑商品",
+    "goodsCategory": "商品分类",
+    "goodsBrand": "商品品牌",
+    "orderList": "订单列表",
+    "orderDetail": "订单详情",
+    "orderRefund": "退款管理",
+    "userList": "用户列表",
+    "userRole": "角色管理",
+    "userPermission": "权限管理",
+    "systemConfig": "系统配置",
+    "systemLog": "系统日志",
+    "systemBackup": "数据备份",
+    "profile": "个人资料",
+    "account": "账户设置",
+    "security": "安全设置"
+  },
+  "form": {
+    "name": "名称",
+    "title": "标题",
+    "description": "描述",
+    "content": "内容",
+    "category": "分类",
+    "status": "状态",
+    "type": "类型",
+    "price": "价格",
+    "stock": "库存",
+    "image": "图片",
+    "sort": "排序",
+    "createTime": "创建时间",
+    "updateTime": "更新时间",
+    "goodsName": "商品名称",
+    "goodsPrice": "商品价格",
+    "goodsStock": "商品库存",
+    "goodsCategory": "商品分类",
+    "goodsBrand": "商品品牌",
+    "goodsImage": "商品图片",
+    "goodsDescription": "商品描述",
+    "goodsStatus": "商品状态",
+    "marketPrice": "市场价",
+    "salePrice": "销售价格",
+    "costPrice": "成本价",
+    "orderNo": "订单号",
+    "orderStatus": "订单状态",
+    "orderAmount": "订单金额",
+    "orderTime": "下单时间",
+    "payTime": "支付时间",
+    "deliveryTime": "发货时间",
+    "username": "用户名",
+    "password": "密码",
+    "email": "邮箱",
+    "phone": "手机号",
+    "avatar": "头像",
+    "role": "角色",
+    "permission": "权限",
+    "required": "此项为必填项",
+    "invalidEmail": "请输入正确的邮箱地址",
+    "invalidPhone": "请输入正确的手机号",
+    "passwordTooShort": "密码长度不能少于6位",
+    "confirmPassword": "确认密码",
+    "passwordNotMatch": "两次输入的密码不一致",
+    "priceRange": "价格区间",
+    "minPrice": "最低价格",
+    "maxPrice": "最高价格",
+    "pleaseInput": "请输入",
+    "pleaseSelect": "请选择",
+    "pleaseUpload": "请上传",
+    "inputName": "请输入商品名称",
+    "inputTitle": "请输入标题",
+    "inputDescription": "请输入描述",
+    "selectCategory": "请选择分类",
+    "selectStatus": "请选择状态",
+    "uploadImage": "请上传图片",
+    "inputOrderNo": "请输入订单编号",
+    "inputUserName": "请输入用户姓名",
+    "inputUserAccount": "请输入用户名或昵称",
+    "inputPhone": "请输入手机号",
+    "inputTrackingNumber": "请输入物流单号",
+    "selectLoginPermission": "请选择登录权限",
+    "selectOrderPermission": "请选择下单权限",
+    "selectWithdrawPermission": "请选择提现权限",
+    "inputCategoryName": "请输入分类名称",
+    "inputSort": "请填写排序",
+    "inputGoodsName": "请输入商品名称(限100字符)",
+    "inputSubtitle": "请输入副标题(限50字符)",
+    "categoryNameRequired": "请填写商品分类名称",
+    "inputGoodsBrand": "请输入商品品牌",
+    "inputGoodsDescription": "请输入商品介绍(限500字符)",
+    "inputGoodsNumber": "请输入商品货号",
+    "inputGoodsSupplier": "请输入商品供应商",
+    "selectShippingTemplate": "请选择运费模板",
+    "uploadMainImage": "上传白底主图",
+    "uploadCarouselImages": "上传轮播图",
+    "uploadDetailImages": "上传详情图",
+    "inputSpecificationName": "请输入规格名称",
+    "inputSpecificationValue": "请输入规格值",
+    "selectSpecification": "请选择规格",
+    "inputNameOrNumber": "请输入名称或编号",
+    "selectSalePrice": "请选择售价(৳)",
+    "selectMarketPrice": "请选择市场价(৳)",
+    "selectPrice": "选择价格",
+    "inputStock": "请输入库存",
+    "inputStockThreshold": "请输入库存预警值",
+    "inputSkuCode": "请输入SKU编码",
+    "categoryRequired": "请选择商品分类",
+    "goodsNameRequired": "请输入商品名称",
+    "goodsBrandRequired": "请输入商品品牌",
+    "shippingTemplateRequired": "请选择运费模板",
+    "goodsNumberRequired": "请输入商品货号",
+    "salePriceRequired": "请输入商品售价",
+    "stockRequired": "请输入商品库存",
+    "goodsStatusRequired": "请选择商品状态",
+    "goodsSupplierRequired": "请输入商品供应商",
+    "mainImageRequired": "请上传商品主图",
+    "carouselImagesRequired": "请上传轮播图",
+    "detailImagesRequired": "请上传商品详情图"
+  },
+  "message": {
+    "loginSuccess": "登录成功",
+    "logoutSuccess": "退出成功",
+    "saveSuccess": "保存成功",
+    "deleteSuccess": "删除成功",
+    "updateSuccess": "更新成功",
+    "createSuccess": "创建成功",
+    "uploadSuccess": "成功上传{count}张图片",
+    "loginFailed": "登录失败",
+    "saveFailed": "保存失败",
+    "deleteFailed": "删除失败",
+    "updateFailed": "更新失败",
+    "createFailed": "创建失败",
+    "uploadFailed": "{count}张图片上传失败",
+    "networkError": "网络错误",
+    "serverError": "服务器错误",
+    "unsavedChanges": "有未保存的更改",
+    "confirmLeave": "确定要离开吗?",
+    "dataWillBeLost": "数据将会丢失",
+    "fieldRequired": "此字段为必填项",
+    "invalidFormat": "格式不正确",
+    "valueTooLong": "输入内容过长",
+    "valueTooShort": "输入内容过短",
+    "confirmDelete": "确定要删除这条记录吗?",
+    "confirmBatchDelete": "确定要删除选中的记录吗?",
+    "confirmSave": "确定要保存吗?",
+    "confirmSubmit": "确定要提交吗?",
+    "confirmReset": "确定要重置吗?",
+    "confirmBatchOnSale": "此操作将批量上架商品,是否继续?",
+    "confirmBatchOffSale": "此操作将批量下架商品,是否继续?",
+    "tip": "提示",
+    "selectionLimitReached": "已到选择上限",
+    "maxUploadLimit": "最多只能上传{count}张图片",
+    "uploadError": "上传失败",
+    "unsupportedFormat": "不支持{extension}格式,请选择{formats}格式的图片",
+    "fileSizeExceeded": "图片大小不能超过{size}MB",
+    "loading": "加载中...",
+    "saving": "保存中...",
+    "deleting": "删除中...",
+    "uploading": "上传中...",
+    "processing": "处理中...",
+    "noData": "暂无数据",
+    "noSearchResult": "没有找到相关数据",
+    "loadMore": "加载更多",
+    "noMore": "没有更多数据了"
+  },
+  "error": {
+    "pageNotFound": "未找到页面",
+    "accessDenied": "访问被拒绝",
+    "unauthorized": "未授权访问",
+    "serverError": "服务器错误",
+    "networkError": "网络错误",
+    "pageNotFoundDesc": "抱歉,您访问的页面不存在",
+    "accessDeniedDesc": "抱歉,您没有权限访问此页面",
+    "unauthorizedDesc": "抱歉,您需要登录后才能访问",
+    "serverErrorDesc": "抱歉,服务器出现错误",
+    "networkErrorDesc": "抱歉,网络连接失败",
+    "goBack": "上一页",
+    "goHome": "返回首页",
+    "retry": "重试",
+    "login": "去登录",
+    "autoCloseIn": "秒后自动关闭",
+    "error401": "401 未授权",
+    "error403": "403 禁止访问",
+    "error404": "404 页面未找到",
+    "error500": "500 服务器错误"
+  },
+  "modules": {
+    "auth": {
+      "login": "登录",
+      "logout": "退出登录",
+      "username": "用户名",
+      "account": "账号",
+      "password": "密码",
+      "confirmPassword": "确认密码",
+      "rememberMe": "记住我",
+      "forgotPassword": "忘记密码",
+      "loginSuccess": "登录成功",
+      "loginFailed": "登录失败",
+      "logoutSuccess": "退出成功",
+      "welcomeTo": "欢迎来到{appName}",
+      "niceToSeeYou": "很高兴再次见到您",
+      "userManagement": "用户管理",
+      "adminList": "管理员列表",
+      "addAdmin": "添加管理员",
+      "editAdmin": "编辑管理员",
+      "deleteAdmin": "删除管理员",
+      "adminName": "管理员姓名",
+      "adminAccount": "管理员账号",
+      "adminRole": "管理员角色",
+      "adminStatus": "管理员状态",
+      "lastLoginTime": "最后登录时间",
+      "roleManagement": "角色管理",
+      "roleList": "角色列表",
+      "addRole": "添加角色",
+      "editRole": "编辑角色",
+      "deleteRole": "删除角色",
+      "roleName": "角色名称",
+      "roleDescription": "角色描述",
+      "rolePermissions": "角色权限",
+      "permissionManagement": "权限管理",
+      "permissionList": "权限列表",
+      "permissionName": "权限名称",
+      "permissionCode": "权限代码",
+      "permissionType": "权限类型",
+      "menuPermission": "菜单权限",
+      "buttonPermission": "按钮权限",
+      "dataPermission": "数据权限",
+      "changePassword": "修改密码",
+      "oldPassword": "原密码",
+      "newPassword": "新密码",
+      "passwordChanged": "密码修改成功",
+      "passwordNotMatch": "两次输入的密码不一致",
+      "passwordTooShort": "密码长度不能少于6位",
+      "usernameRequired": "请输入用户名",
+      "accountRequired": "请输入账号",
+      "passwordRequired": "请输入密码",
+      "confirmPasswordRequired": "请确认密码",
+      "roleRequired": "请选择角色",
+      "normal": "正常",
+      "disabled": "禁用",
+      "locked": "锁定"
+    },
+    "dashboard": {
+      "dashboard": "仪表盘",
+      "overview": "概览",
+      "statistics": "统计",
+      "todayData": "今日数据",
+      "yesterdayData": "昨日数据",
+      "thisWeekData": "本周数据",
+      "thisMonthData": "本月数据",
+      "salesData": "销售数据",
+      "salesAmount": "销售额",
+      "orderCount": "订单数",
+      "userCount": "用户数",
+      "goodsCount": "商品数",
+      "salesTrend": "销售趋势",
+      "orderTrend": "订单趋势",
+      "userTrend": "用户趋势",
+      "topGoods": "热销商品",
+      "topCategories": "热门分类",
+      "topUsers": "活跃用户",
+      "pendingTasks": "待处理事项",
+      "pendingOrders": "待处理订单",
+      "pendingRefunds": "待处理退款",
+      "lowStockGoods": "库存不足商品",
+      "quickActions": "快捷操作",
+      "addGoods": "添加商品",
+      "processOrder": "处理订单",
+      "viewReport": "查看报表",
+      "systemSettings": "系统设置",
+      "today": "今天",
+      "yesterday": "昨天",
+      "last7Days": "最近7天",
+      "last30Days": "最近30天",
+      "thisMonth": "本月",
+      "lastMonth": "上月",
+      "yuan": "元",
+      "orders": "单",
+      "users": "人",
+      "pieces": "件",
+      "percent": "%",
+      "growthRate": "增长率",
+      "increase": "增长",
+      "decrease": "下降",
+      "noChange": "持平"
+    },
+    "goods": {
+      "goodsList": "商品列表",
+      "goodsId": "商品ID",
+      "basicInfo": "基本信息",
+      "goodsInfo": "商品信息",
+      "goodsAttributes": "商品属性",
+      "goodsName": "商品名称",
+      "goodsSubtitle": "副标题",
+      "subtitle": "副标题",
+      "goodsBrand": "商品品牌",
+      "originalPrice": "原价",
+      "onlinePrice": "在线价",
+      "alreadyJoined": "已参加",
+      "join": "参加",
+      "goodsCategory": "商品分类",
+      "categoryName": "分类名称",
+      "parentCategory": "上级分类",
+      "categorySort": "分类排序",
+      "categoryStatus": "分类状态",
+      "categoryDescription": "分类描述",
+      "addCategory": "新建分类",
+      "editCategory": "编辑分类",
+      "goodsDescription": "商品介绍",
+      "goodsNumber": "商品货号",
+      "goodsSupplier": "商品供应商",
+      "defaultSupplier": "商家科技有限公司",
+      "sales": "销量",
+      "shippingTemplate": "运费模板",
+      "freeShipping": "包邮",
+      "mainImage": "商品主图",
+      "carouselImages": "轮播图",
+      "detailImages": "详情图",
+      "autoGenerateNumberTip": "如果您不输入商品货号,系统将自动生成一个唯一的货号",
+      "newGoodsOffSaleTip": "新增商品默认为下架状态,保存后可修改",
+      "priceStock": "价格库存",
+      "salePrice": "销售价格",
+      "marketPrice": "市场价",
+      "costPrice": "成本价",
+      "vipPrice": "会员价格",
+      "stock": "商品库存",
+      "goodsStock": "商品库存",
+      "stockThreshold": "库存预警值",
+      "skuCode": "SKU编码",
+      "image": "图片",
+      "goodsStatus": "商品状态",
+      "onSale": "上架",
+      "offSale": "下架",
+      "inStock": "有库存",
+      "outOfStock": "缺货",
+      "batchOnSale": "批量上架",
+      "batchOffSale": "批量下架",
+      "batchDelete": "批量删除",
+      "specificationSettings": "商品规格设置",
+      "specificationName": "规格名称",
+      "specificationValue": "规格值",
+      "addSpecification": "添加规格",
+      "addSpecificationValue": "添加规格值",
+      "batchSettings": "批量设置",
+      "batchSettingsTip": "未选择规格默认为全选批量设置",
+      "goodsAddedSuccess": "商品添加成功,请等上架",
+      "continueAdd": "继续添加",
+      "savingGoods": "正在保存商品...",
+      "pleaseWait": "请稍候,系统正在处理您的请求",
+      "specificationValueExists": "子规格已存在",
+      "batchSettingsSuccess": "批量设置成功",
+      "pleaseAddSpecification": "请先添加商品规格",
+      "specificationSalePriceError": "第{index}个规格的销售价格不能为空且必须大于0",
+      "specificationMarketPriceError": "第{index}个规格的市场价不能为空且必须大于0",
+      "specificationStockError": "第{index}个规格的商品库存不能为空且不能小于0",
+      "pleaseCompleteBasicInfo": "请完善商品基本信息",
+      "goodsSaveSuccess": "商品保存成功",
+      "sliderImage": "轮播图",
+      "detailImage": "详情图",
+      "whiteBackgroundImage": "白底主图",
+      "uploadMainImage": "上传商品主图",
+      "uploadSliderImage": "上传轮播图",
+      "uploadDetailImage": "上传详情图",
+      "specification": "商品规格",
+      "specType": "规格类型",
+      "singleSpec": "单规格",
+      "multiSpec": "多规格",
+      "specName": "规格名称",
+      "specValue": "规格值",
+      "addSpec": "添加规格",
+      "addSpecValue": "添加规格值",
+      "batchSetting": "批量设置",
+      "skuPrice": "SKU价格",
+      "skuStock": "SKU库存",
+      "skuImage": "SKU图片",
+      "addGoods": "添加商品",
+      "editGoods": "编辑商品",
+      "deleteGoods": "删除商品",
+      "goodsAddSuccess": "商品添加成功",
+      "goodsUpdateSuccess": "商品更新成功",
+      "goodsDeleteSuccess": "商品删除成功",
+      "pleaseSelectGoods": "请选择商品",
+      "confirmDeleteGoods": "确定要删除选中的商品吗?",
+      "goodsNameRequired": "请填写商品名称",
+      "goodsBrandRequired": "请填写商品品牌",
+      "goodsCategoryRequired": "请选择商品分类",
+      "goodsPriceRequired": "请填写商品售价",
+      "goodsStockRequired": "请填写商品库存",
+      "goodsSupplierRequired": "请填写商品供应商",
+      "goodsImageRequired": "请上传商品主图",
+      "goodsDetailImageRequired": "请上传商品详情图"
+    },
+    "order": {
+      "orderManagement": "订单管理",
+      "orderList": "订单列表",
+      "orderDetail": "订单详情",
+      "orderNo": "订单号",
+      "orderStatus": "订单状态",
+      "orderAmount": "订单金额",
+      "orderTime": "下单时间",
+      "payTime": "支付时间",
+      "deliveryTime": "发货时间",
+      "completeTime": "完成时间",
+      "pendingPayment": "待付款",
+      "paid": "已支付",
+      "pendingDelivery": "待发货",
+      "pendingReceive": "待收货",
+      "completed": "已完成",
+      "cancelled": "已取消",
+      "refunded": "已退款",
+      "closed": "已关闭",
+      "buyerInfo": "买家信息",
+      "buyerName": "买家姓名",
+      "buyerPhone": "买家电话",
+      "buyerAddress": "收货地址",
+      "goodsInfo": "商品信息",
+      "goodsName": "商品名称",
+      "goodsPrice": "商品价格",
+      "goodsQuantity": "商品数量",
+      "goodsTotal": "商品小计",
+      "subtotal": "商品小计",
+      "shippingFee": "运费",
+      "discount": "优惠金额",
+      "totalAmount": "总金额",
+      "actualPayment": "实付金额",
+      "logistics": "物流信息",
+      "trackingNumber": "快递单号",
+      "courier": "快递公司",
+      "shippingAddress": "发货地址",
+      "receivingAddress": "收货地址",
+      "aftersale": "售后管理",
+      "refund": "退款",
+      "return": "退货",
+      "exchange": "换货",
+      "refundAmount": "退款金额",
+      "refundReason": "退款原因",
+      "refundStatus": "退款状态",
+      "invoice": "发票管理",
+      "invoiceType": "发票类型",
+      "invoiceTitle": "发票抬头",
+      "invoiceContent": "发票内容",
+      "processOrder": "处理订单",
+      "shipOrder": "发货",
+      "cancelOrder": "取消订单",
+      "refundOrder": "退款",
+      "viewDetail": "查看详情",
+      "printOrder": "打印订单",
+      "exportOrder": "导出订单"
+    },
+    "system": {
+      "systemManagement": "系统管理",
+      "systemSettings": "系统设置",
+      "systemConfig": "系统配置",
+      "basicSettings": "基本设置",
+      "siteName": "站点名称",
+      "siteDescription": "站点描述",
+      "siteKeywords": "站点关键词",
+      "siteLogo": "站点Logo",
+      "siteIcon": "站点图标",
+      "bannerManagement": "轮播图管理",
+      "bannerList": "轮播图列表",
+      "addBanner": "添加轮播图",
+      "editBanner": "编辑轮播图",
+      "deleteBanner": "删除轮播图",
+      "bannerTitle": "轮播图标题",
+      "bannerImage": "轮播图片",
+      "bannerLink": "跳转链接",
+      "bannerSort": "排序",
+      "paymentConfig": "支付配置",
+      "paymentMethod": "支付方式",
+      "alipay": "支付宝",
+      "wechatPay": "微信支付",
+      "bankCard": "银行卡",
+      "paymentStatus": "支付状态",
+      "notificationManagement": "通知管理",
+      "systemNotification": "系统通知",
+      "userNotification": "用户通知",
+      "notificationTitle": "通知标题",
+      "notificationContent": "通知内容",
+      "notificationType": "通知类型",
+      "smsManagement": "短信管理",
+      "smsTemplate": "短信模板",
+      "smsRecord": "短信记录",
+      "smsContent": "短信内容",
+      "smsStatus": "发送状态",
+      "dataReport": "数据报表",
+      "salesReport": "销售报表",
+      "userReport": "用户报表",
+      "goodsReport": "商品报表",
+      "systemLog": "系统日志",
+      "operationLog": "操作日志",
+      "loginLog": "登录日志",
+      "errorLog": "错误日志",
+      "systemMonitor": "系统监控",
+      "serverStatus": "服务器状态",
+      "databaseStatus": "数据库状态",
+      "cacheStatus": "缓存状态",
+      "backupRestore": "备份恢复",
+      "dataBackup": "数据备份",
+      "dataRestore": "数据恢复",
+      "backupTime": "备份时间",
+      "backupSize": "备份大小",
+      "versionInfo": "版本信息",
+      "currentVersion": "当前版本",
+      "updateTime": "更新时间",
+      "enable": "启用",
+      "disable": "禁用",
+      "configure": "配置",
+      "test": "测试",
+      "backup": "备份",
+      "restore": "恢复"
+    },
+    "user": {
+      "userManagement": "用户管理",
+      "userList": "用户列表",
+      "userDetail": "用户详情",
+      "addUser": "添加用户",
+      "editUser": "编辑用户",
+      "deleteUser": "删除用户",
+      "userId": "用户ID",
+      "userName": "用户姓名",
+      "userAccount": "用户账号",
+      "userNickname": "用户昵称",
+      "userPhone": "手机号码",
+      "userEmail": "邮箱地址",
+      "userAvatar": "用户头像",
+      "userGender": "性别",
+      "userBirthday": "生日",
+      "userAddress": "地址",
+      "male": "男",
+      "female": "女",
+      "unknown": "未知",
+      "userStatus": "用户状态",
+      "active": "正常",
+      "inactive": "禁用",
+      "frozen": "冻结",
+      "userLevel": "用户等级",
+      "levelManagement": "等级管理",
+      "levelName": "等级名称",
+      "levelDiscount": "等级折扣",
+      "levelCondition": "升级条件",
+      "userTag": "用户标签",
+      "tagManagement": "标签管理",
+      "tagName": "标签名称",
+      "tagColor": "标签颜色",
+      "tagDescription": "标签描述",
+      "totalUsers": "总用户数",
+      "activeUsers": "活跃用户",
+      "newUsers": "新增用户",
+      "registrationTime": "注册时间",
+      "lastLoginTime": "最后登录",
+      "loginCount": "登录次数",
+      "userBehavior": "用户行为",
+      "orderCount": "订单数量",
+      "totalConsumption": "消费总额",
+      "averageOrderValue": "客单价",
+      "loginPermission": "登录权限",
+      "orderPermission": "下单权限",
+      "withdrawPermission": "提现权限",
+      "viewProfile": "查看资料",
+      "editProfile": "编辑资料",
+      "resetPassword": "重置密码",
+      "enableUser": "启用用户",
+      "disableUser": "禁用用户",
+      "freezeUser": "冻结用户",
+      "phoneRequired": "请输入手机号码",
+      "emailRequired": "请输入邮箱地址",
+      "nameRequired": "请输入用户姓名",
+      "invalidPhone": "手机号码格式不正确",
+      "invalidEmail": "邮箱格式不正确"
+    }
+  }
+}

+ 14 - 2
src/main.js

@@ -6,6 +6,9 @@ import { setupRouter } from '@/sheep/router';
 // element-plus
 import ElementPlus from 'element-plus';
 import zhCn from 'element-plus/dist/locale/zh-cn.mjs';
+import en from 'element-plus/dist/locale/en.mjs';
+// i18n
+import i18n from '@/locales';
 
 // css reset
 import 'normalize.css/normalize.css';
@@ -26,8 +29,17 @@ async function bootstrap() {
   }
   const app = createApp(App);
 
-  // element-plus
-  app.use(ElementPlus, { locale: zhCn });
+  // i18n
+  app.use(i18n);
+
+  // element-plus with dynamic locale
+  const elementLocales = {
+    'zh-CN': zhCn,
+    'en-US': en,
+  };
+  app.use(ElementPlus, {
+    locale: elementLocales[i18n.global.locale.value] || zhCn,
+  });
 
   // pinia store
   setupStore(app);

+ 7 - 3
src/sheep/components/sa-table/sa-search/sa-search-simple.global.vue

@@ -123,10 +123,10 @@
 
           <!-- 操作按钮 -->
           <el-form-item>
-            <el-button type="primary" @click="handleSearch">搜索</el-button>
-            <el-button @click="handleReset">重置</el-button>
+            <el-button type="primary" @click="handleSearch">{{ t('common.search') }}</el-button>
+            <el-button @click="handleReset">{{ t('common.reset') }}</el-button>
             <el-button v-if="hasMoreThanThree" type="text" @click="toggleExpand">
-              {{ isExpanded ? '收起' : '展开' }}
+              {{ isExpanded ? t('common.collapse') : t('common.expand') }}
               <el-icon class="expand-toggle-icon" :class="{ 'is-expanded': isExpanded }">
                 <ArrowDown />
               </el-icon>
@@ -151,6 +151,10 @@
 </script>
 
 <script setup>
+  import { useI18n } from 'vue-i18n';
+
+  const { t } = useI18n();
+
   const props = defineProps({
     searchFields: {
       type: Object,

+ 37 - 16
src/sheep/components/sa-uploader/sa-upload-image.global.vue

@@ -34,16 +34,23 @@
             </el-image>
             <div class="image-mask" :class="{ 'compact-mask': compact }">
               <template v-if="compact">
-                <el-icon @click.stop="removeImage(index)" title="删除" size="12"
+                <el-icon @click.stop="removeImage(index)" :title="t('common.delete')" size="12"
                   ><Delete
                 /></el-icon>
               </template>
               <template v-else>
-                <el-icon class="sortable-drag" title="拖拽排序" size="24"><Rank /></el-icon>
-                <el-icon @click="previewImage(element, index)" title="预览" size="24"
+                <el-icon class="sortable-drag" :title="t('common.dragSort')" size="24"
+                  ><Rank
+                /></el-icon>
+                <el-icon
+                  @click="previewImage(element, index)"
+                  :title="t('common.preview')"
+                  size="24"
                   ><ZoomIn
                 /></el-icon>
-                <el-icon @click="removeImage(index)" title="删除" size="24"><Delete /></el-icon>
+                <el-icon @click="removeImage(index)" :title="t('common.delete')" size="24"
+                  ><Delete
+                /></el-icon>
               </template>
             </div>
           </div>
@@ -65,13 +72,15 @@
           <el-icon class="upload-icon" :size="compact ? 16 : 24">
             <Plus />
           </el-icon>
-          <div v-if="!compact" class="upload-text">{{ placeholder || '上传图片' }}</div>
+          <div v-if="!compact" class="upload-text">{{
+            placeholder || t('common.uploadImage')
+          }}</div>
         </div>
 
         <!-- 自定义loading遮罩 -->
         <div v-if="uploading" class="upload-loading">
           <div class="loading-spinner"></div>
-          <div v-if="!compact" class="loading-text">上传中...</div>
+          <div v-if="!compact" class="loading-text">{{ t('common.uploading') }}</div>
         </div>
       </div>
 
@@ -88,9 +97,11 @@
 
     <!-- 提示信息 -->
     <div v-if="showTip" class="upload-tip">
-      <span v-if="maxCount">最多上传{{ maxCount }}张,</span>
-      <span v-if="accept && accept.length">支持{{ accept.join('、') }}格式,</span>
-      <span v-if="maxSize">单张图片不超过{{ maxSize }}MB</span>
+      <span v-if="maxCount">{{ t('common.maxUpload', { count: maxCount }) }},</span>
+      <span v-if="accept && accept.length"
+        >{{ t('common.supportFormats', { formats: accept.join('、') }) }},</span
+      >
+      <span v-if="maxSize">{{ t('common.maxFileSize', { size: maxSize }) }}</span>
     </div>
 
     <!-- 图片预览弹窗 -->
@@ -129,6 +140,10 @@
 </script>
 
 <script setup>
+  import { useI18n } from 'vue-i18n';
+
+  const { t } = useI18n();
+
   const props = defineProps({
     modelValue: {
       type: Array,
@@ -269,7 +284,7 @@
 
     // 检查数量限制
     if (props.maxCount && imageList.value.length + files.length > props.maxCount) {
-      ElMessage.warning(`最多只能上传${props.maxCount}张图片`);
+      ElMessage.warning(t('message.maxUploadLimit', { count: props.maxCount }));
       return;
     }
 
@@ -300,16 +315,16 @@
 
       const successCount = results.filter((r) => r.success).length;
       if (successCount > 0) {
-        ElMessage.success(`成功上传${successCount}张图片`);
+        ElMessage.success(t('message.uploadSuccess', { count: successCount }));
       }
 
       // 如果有失败的,显示失败信息
       const failedCount = results.length - successCount;
       if (failedCount > 0) {
-        ElMessage.error(`${failedCount}张图片上传失败`);
+        ElMessage.error(t('message.uploadFailed', { count: failedCount }));
       }
     } catch (error) {
-      ElMessage.error('上传失败:' + error.message);
+      ElMessage.error(t('message.uploadError') + ': ' + error.message);
     } finally {
       // 无论成功还是失败,都关闭loading
       uploading.value = false;
@@ -324,14 +339,19 @@
     // 检查文件类型
     const fileExtension = file.name.split('.').pop().toLowerCase();
     if (!props.accept.includes(fileExtension)) {
-      ElMessage.warning(`不支持${fileExtension}格式,请选择${props.accept.join('、')}格式的图片`);
+      ElMessage.warning(
+        t('message.unsupportedFormat', {
+          extension: fileExtension,
+          formats: props.accept.join('、'),
+        }),
+      );
       return false;
     }
 
     // 检查文件大小
     const fileSizeMB = file.size / 1024 / 1024;
     if (props.maxSize && fileSizeMB > props.maxSize) {
-      ElMessage.warning(`图片大小不能超过${props.maxSize}MB`);
+      ElMessage.warning(t('message.fileSizeExceeded', { size: props.maxSize }));
       return false;
     }
 
@@ -350,7 +370,7 @@
           url: response.data,
         };
       } else {
-        throw new Error(response.msg || '上传失败');
+        throw new Error(response.msg || t('message.uploadFailed'));
       }
     } catch (error) {
       console.error('上传文件失败:', error);
@@ -505,6 +525,7 @@
         font-size: 12px;
         color: #8c939d;
         transition: all 0.3s;
+        text-align: center;
       }
     }
 

+ 1 - 1
src/sheep/layouts/index.vue

@@ -69,7 +69,7 @@
   import sheep from '@/sheep';
 
   const appStore = sheep.$store('app');
-  const { appInited, appInfo, appLayout, requestCounter, menuStyle, appChildrenMenu } = useApp();
+  const { appInited, appInfo, appLayout, requestCounter } = useApp();
   const taskbarHistory = computed(() => appStore.taskbar.history);
   const rotateSpeed = computed(() => {
     let speed = 0;

+ 81 - 0
src/sheep/layouts/taskbar/index.vue

@@ -37,6 +37,28 @@
         </div>
       </div>
     </div>
+
+    <!-- 语言切换器 -->
+    <div class="app-process__language">
+      <el-dropdown @command="handleLanguageChange" class="language-switcher">
+        <div class="language-trigger">
+          <span class="language-icon">🌐</span>
+          <span class="language-text">{{ currentLanguageLabel }}</span>
+          <el-icon class="dropdown-icon"><ArrowDown /></el-icon>
+        </div>
+        <template #dropdown>
+          <el-dropdown-menu>
+            <el-dropdown-item command="zh-CN" :class="{ active: locale === 'zh-CN' }">
+              🇨🇳 中文
+            </el-dropdown-item>
+            <el-dropdown-item command="en-US" :class="{ active: locale === 'en-US' }">
+              🇺🇸 English
+            </el-dropdown-item>
+          </el-dropdown-menu>
+        </template>
+      </el-dropdown>
+    </div>
+
     <!-- 用户头像下拉菜单 -->
     <div class="app-process__user">
       <el-dropdown @command="handleCommand" trigger="click">
@@ -90,6 +112,7 @@
   import { ElMessageBox, ElMessage } from 'element-plus';
   import { ArrowDown, User, SwitchButton, Lock } from '@element-plus/icons-vue';
   import PasswordEdit from '@/app/admin/views/auth/admin/password.vue';
+  import { useTranslation } from '@/sheep/utils/i18n';
 
   const router = useRouter();
   const selectedCM = ref(0);
@@ -101,6 +124,10 @@
   const accountStore = sheep.$store('account');
   const userInfo = computed(() => accountStore.info);
 
+  // 语言切换相关
+  const { locale, changeLanguage, getCurrentLanguageLabel } = useTranslation();
+  const currentLanguageLabel = computed(() => getCurrentLanguageLabel());
+
   // 点击菜单
   function onMenu(item) {
     router.push({
@@ -139,6 +166,11 @@
     }
   }
 
+  // 处理语言切换
+  function handleLanguageChange(command) {
+    changeLanguage(command);
+  }
+
   // 修改密码
   function changePassword() {
     // 检查用户ID是否为1
@@ -205,6 +237,55 @@
       }
     }
 
+    &__language {
+      flex-shrink: 0;
+      padding-right: 12px;
+
+      .language-switcher {
+        .language-trigger {
+          display: flex;
+          align-items: center;
+          cursor: pointer;
+          padding: 8px 12px;
+          border-radius: 6px;
+          transition: all 0.3s ease;
+
+          &:hover {
+            background-color: var(--el-color-primary-light-9);
+            color: var(--el-color-primary);
+          }
+
+          .language-icon {
+            font-size: 16px;
+            margin-right: 6px;
+          }
+
+          .language-text {
+            font-size: 14px;
+            font-weight: 500;
+            margin-right: 4px;
+          }
+
+          .dropdown-icon {
+            font-size: 12px;
+            color: var(--el-text-color-regular);
+            transition: transform 0.3s ease;
+          }
+
+          &:hover .dropdown-icon {
+            color: var(--el-color-primary);
+          }
+        }
+
+        :deep(.el-dropdown-menu__item) {
+          &.active {
+            color: var(--el-color-primary);
+            background-color: var(--el-color-primary-light-9);
+          }
+        }
+      }
+    }
+
     &__user {
       flex-shrink: 0;
       padding-right: 16px;

+ 70 - 0
src/sheep/utils/i18n.js

@@ -0,0 +1,70 @@
+import { useI18n } from 'vue-i18n'
+import { setLanguage } from '@/locales'
+
+/**
+ * 多语言工具函数
+ */
+export function useTranslation() {
+  const { t, locale } = useI18n()
+  
+  /**
+   * 切换语言
+   * @param {string} lang 语言代码 zh-CN | en-US
+   */
+  const changeLanguage = (lang) => {
+    setLanguage(lang)
+    // 可选:刷新页面以确保所有组件重新渲染
+    // window.location.reload()
+  }
+  
+  /**
+   * 获取当前语言标签
+   */
+  const getCurrentLanguageLabel = () => {
+    const labels = {
+      'zh-CN': '中文',
+      'en-US': 'English'
+    }
+    return labels[locale.value] || '中文'
+  }
+  
+  /**
+   * 获取语言选项列表
+   */
+  const getLanguageOptions = () => {
+    return [
+      { value: 'zh-CN', label: '🇨🇳 中文' },
+      { value: 'en-US', label: '🇺🇸 English' }
+    ]
+  }
+  
+  return {
+    t,
+    locale,
+    changeLanguage,
+    getCurrentLanguageLabel,
+    getLanguageOptions
+  }
+}
+
+/**
+ * 语言切换 Composable
+ */
+export function useLanguageSwitcher() {
+  const { locale } = useI18n()
+  
+  const switchLanguage = (newLocale) => {
+    setLanguage(newLocale)
+  }
+  
+  const toggleLanguage = () => {
+    const newLocale = locale.value === 'zh-CN' ? 'en-US' : 'zh-CN'
+    switchLanguage(newLocale)
+  }
+  
+  return {
+    locale,
+    switchLanguage,
+    toggleLanguage
+  }
+}

+ 6 - 3
src/sheep/views/error/401.vue

@@ -2,11 +2,11 @@
   <div class="error-index panel-block sa-flex sa-row-center">
     <img src="/static/images/error/401.png" />
     <div>
-      未找到页面,
+      {{ t('error.unauthorized') }},
       <span>
-        <span>{{ second }}</span> 秒后自动关闭
+        <span>{{ second }}</span> {{ t('error.autoCloseIn') }}
       </span>
-      <span class="close" @click="close">上一页</span>
+      <span class="close" @click="close">{{ t('error.goBack') }}</span>
     </div>
   </div>
 </template>
@@ -14,6 +14,9 @@
   import { ref } from 'vue';
   import sheep from '@/sheep';
   import { closeTaskbar } from '@/sheep/hooks/useTaskbar';
+  import { useI18n } from 'vue-i18n';
+
+  const { t } = useI18n();
 
   const currentPageIndex = sheep
     .$store('app')

+ 6 - 3
src/sheep/views/error/403.vue

@@ -2,11 +2,11 @@
   <div class="error-index panel-block sa-flex sa-row-center">
     <img src="/static/images/error/403.png" />
     <div>
-      未找到页面,
+      {{ t('error.accessDenied') }},
       <span>
-        <span>{{ second }}</span> 秒后自动关闭
+        <span>{{ second }}</span> {{ t('error.autoCloseIn') }}
       </span>
-      <span class="close" @click="close">上一页</span>
+      <span class="close" @click="close">{{ t('error.goBack') }}</span>
     </div>
   </div>
 </template>
@@ -14,6 +14,9 @@
   import { ref } from 'vue';
   import { useRoute } from 'vue-router';
   import sheep from '@/sheep';
+  import { useI18n } from 'vue-i18n';
+
+  const { t } = useI18n();
 
   const appStore = sheep.$store('app');
 

+ 6 - 3
src/sheep/views/error/404.vue

@@ -2,11 +2,11 @@
   <div class="error-index panel-block sa-flex sa-row-center">
     <img src="/static/images/error/404.png" />
     <div>
-      未找到页面,
+      {{ t('error.pageNotFound') }},
       <span>
-        <span>{{ second }}</span> 秒后自动关闭
+        <span>{{ second }}</span> {{ t('error.autoCloseIn') }}
       </span>
-      <span class="close" @click="close">上一页</span>
+      <span class="close" @click="close">{{ t('error.goBack') }}</span>
     </div>
   </div>
 </template>
@@ -14,6 +14,9 @@
   import { ref } from 'vue';
   import sheep from '@/sheep';
   import { closeTaskbar } from '@/sheep/hooks/useTaskbar';
+  import { useI18n } from 'vue-i18n';
+
+  const { t } = useI18n();
 
   const currentPageIndex = sheep
     .$store('app')

+ 17 - 12
src/sheep/views/login/index.vue

@@ -11,8 +11,8 @@
           <div v-if="login.type == 'input'" class="login-wrap sa-flex sa-flex-center sa-flex-1">
             <div class="admin-name">
               <div>
-                <div class="sa-line-1">欢迎来到{{ appName }}!</div>
-                <div class="admin-welcome">很高兴再次见到您</div>
+                <div class="sa-line-1">{{ t('modules.auth.welcomeTo', { appName }) }}!</div>
+                <div class="admin-welcome">{{ t('modules.auth.niceToSeeYou') }}</div>
               </div>
             </div>
             <el-form
@@ -25,7 +25,7 @@
               <el-form-item prop="account">
                 <el-input :class="form.data.account ? 'is-focus' : ''" v-model="form.data.account">
                   <template #prefix>
-                    <div class="label">账号</div>
+                    <div class="label">{{ t('modules.auth.account') }}</div>
                   </template>
                 </el-input>
               </el-form-item>
@@ -37,7 +37,7 @@
                   autocomplete="new-password"
                 >
                   <template #prefix>
-                    <div class="label">密码</div>
+                    <div class="label">{{ t('modules.auth.password') }}</div>
                   </template>
                   <template #suffix>
                     <span v-if="showPwd" @click="showPwd = false">
@@ -54,11 +54,11 @@
                 <el-checkbox
                   v-model="login.last.rememberMe"
                   @change="changeRememberMe"
-                  label="记住我"
+                  :label="t('modules.auth.rememberMe')"
                 ></el-checkbox>
               </div>
-              <el-button type="primary" :loading="loginLoading" @click="accountLogin"
-                >登录
+              <el-button type="primary" :loading="loginLoading" @click="accountLogin">
+                {{ t('modules.auth.login') }}
               </el-button>
             </el-form>
           </div>
@@ -90,7 +90,7 @@
                   autocomplete="new-password"
                 >
                   <template #prefix>
-                    <div class="label">密码</div>
+                    <div class="label">{{ t('modules.auth.password') }}</div>
                   </template>
                   <template #suffix>
                     <span v-if="showPwd" @click="showPwd = false">
@@ -125,6 +125,9 @@
   import sheep from '@/sheep';
   import { checkUrl } from '@/sheep/utils/checkUrlSuffix';
   import { ElMessage } from 'element-plus';
+  import { useI18n } from 'vue-i18n';
+
+  const { t } = useI18n();
 
   const accountStore = sheep.$store('account');
   const appStore = sheep.$store('app');
@@ -144,9 +147,11 @@
       account: '',
       pwd: '',
     },
-    rules: {
-      account: [{ required: true, message: '请输入账号', trigger: 'blur' }],
-      pwd: [{ required: true, message: '请输入密码', trigger: 'blur' }],
+    get rules() {
+      return {
+        account: [{ required: true, message: t('modules.auth.accountRequired'), trigger: 'blur' }],
+        pwd: [{ required: true, message: t('modules.auth.passwordRequired'), trigger: 'blur' }],
+      };
     },
   });
 
@@ -160,7 +165,7 @@
         let submit = form.data;
         const { code } = await accountStore.login(submit);
         if (code == '200') {
-          ElMessage.success('登录成功');
+          ElMessage.success(t('modules.auth.loginSuccess'));
           storage.set('lastLogin', login.last);
           await appStore.appLoad();
           router.push('/');