diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..21c1781 --- /dev/null +++ b/.env.example @@ -0,0 +1,10 @@ +# Docker Compose database settings +MYSQL_ROOT_PASSWORD=replace-with-a-strong-root-password +MYSQL_DATABASE=huayang +MYSQL_USER=huayang +MYSQL_PASSWORD=replace-with-a-strong-application-password + +# Spring datasource settings +DB_URL=jdbc:mysql://localhost:3306/huayang?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true +DB_USERNAME=huayang +DB_PASSWORD=replace-with-a-strong-application-password diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b0213b0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,49 @@ +# Dependencies +node_modules/ +vendor/ +.venv/ +venv/ +__pycache__/ +.gradle/ + +# Build outputs +target/ +build/ +!admin/src/views/tool/build/ +!admin/src/views/tool/build/** +dist/ +out/ +coverage/ + +# Environment and local runtime data +.env +.env.* +!.env.example +logs/ +*.log +*.out +*.err +uploadPath/ +mysql-data/ + +# Editors and operating systems +.idea/ +.vscode/ +*.swp +*.swo +.DS_Store +Thumbs.db + +# Temporary and archive files +*.tmp +*.bak +*.cache +*.zip +~$* +/.codex_tmp/ +/package-lock.json + +# AI auxiliary files +AI_CONTEXT.md +.ai_context.md +.codex/ diff --git a/PROJECT_PROGRESS.md b/PROJECT_PROGRESS.md new file mode 100644 index 0000000..5a525f5 --- /dev/null +++ b/PROJECT_PROGRESS.md @@ -0,0 +1,30 @@ + + +# 项目进度 + +## 当前阶段 + +- 阶段:开发与联调 +- 整体完成度:75%(估算) + +## 模块状态 + +| 模块 | 状态 | 完成度 | +| --- | --- | ---: | +| 公众网站 | 进行中 | 80% | +| 管理后台 | 进行中 | 75% | +| 后端接口 | 进行中 | 80% | +| 多语言与翻译 | 进行中 | 75% | +| 数据库脚本 | 进行中 | 75% | +| 部署配置 | 进行中 | 60% | + +## 当前任务 + +- 整理并提交官网、管理后台、后端、国际化及数据库脚本变更。 +- 清理版本库中的本地环境文件和 Maven 构建产物。 +- 官网、管理后台和后端构建验证已完成,国际化键同步检查已通过。 + +## 下一步 + +- 执行核心接口与多语言发布流程的集成测试。 +- 在目标环境验证数据库迁移和容器部署配置。 diff --git a/admin/.env.development b/admin/.env.development deleted file mode 100644 index 0c8290e..0000000 --- a/admin/.env.development +++ /dev/null @@ -1,14 +0,0 @@ -# 页面标题 -VUE_APP_TITLE = 华阳食品管理系统 - -# 开发环境配置 -ENV = 'development' - -# 华阳食品管理系统/开发环境 -VUE_APP_BASE_API = '/dev-api' - -# 路由懒加载 -VUE_CLI_BABEL_TRANSPILE_MODULES = true - -# 门户站点根地址(首页工作台「打开门户首页」按钮,默认本地 Vite) -VUE_APP_PORTAL_URL=http://localhost:5173/ diff --git a/admin/.env.production b/admin/.env.production deleted file mode 100644 index 1cd0374..0000000 --- a/admin/.env.production +++ /dev/null @@ -1,11 +0,0 @@ -# 页面标题 -VUE_APP_TITLE = 华阳食品管理系统 - -# 生产环境配置 -ENV = 'production' - -# 华阳食品管理系统/生产环境 -VUE_APP_BASE_API = '/prod-api' - -# 门户官网地址(首页工作台预览;生产环境请改为实际域名,如 https://www.example.com/) -# VUE_APP_PORTAL_URL=https://www.example.com/ diff --git a/admin/.env.staging b/admin/.env.staging deleted file mode 100644 index aa02c5c..0000000 --- a/admin/.env.staging +++ /dev/null @@ -1,12 +0,0 @@ -# 页面标题 -VUE_APP_TITLE = 华阳食品管理系统 - -BABEL_ENV = production - -NODE_ENV = production - -# 测试环境配置 -ENV = 'staging' - -# 华阳食品管理系统/测试环境 -VUE_APP_BASE_API = '/stage-api' diff --git a/admin/package.json b/admin/package.json index a6ea29d..3a86b1e 100644 --- a/admin/package.json +++ b/admin/package.json @@ -6,6 +6,7 @@ "license": "MIT", "scripts": { "dev": "vue-cli-service serve", + "build": "vue-cli-service build", "build:prod": "vue-cli-service build", "build:stage": "vue-cli-service build --mode staging", "preview": "node build/index.js --preview" @@ -43,7 +44,7 @@ "vue": "2.6.12", "vue-count-to": "1.0.13", "vue-cropper": "0.5.5", - "vue-i18n": "^8.28.2", + "vue-i18n": "^7.3.2", "vue-router": "3.4.9", "vuedraggable": "2.24.3", "vuex": "3.6.0" diff --git a/admin/src/api/huayang/companyTech.js b/admin/src/api/huayang/companyTech.js deleted file mode 100644 index 65d3da9..0000000 --- a/admin/src/api/huayang/companyTech.js +++ /dev/null @@ -1,39 +0,0 @@ -import request from '@/utils/request' - -export function listCompanyTech(query) { - return request({ - url: '/huayang/companyTech/list', - method: 'get', - params: query - }) -} - -export function getCompanyTech(techId) { - return request({ - url: '/huayang/companyTech/' + techId, - method: 'get' - }) -} - -export function addCompanyTech(data) { - return request({ - url: '/huayang/companyTech', - method: 'post', - data: data - }) -} - -export function updateCompanyTech(data) { - return request({ - url: '/huayang/companyTech', - method: 'put', - data: data - }) -} - -export function delCompanyTech(techId) { - return request({ - url: '/huayang/companyTech/' + techId, - method: 'delete' - }) -} diff --git a/admin/src/api/huayang/joinApply.js b/admin/src/api/huayang/joinApply.js deleted file mode 100644 index 7985f43..0000000 --- a/admin/src/api/huayang/joinApply.js +++ /dev/null @@ -1,31 +0,0 @@ -import request from '@/utils/request' - -export function listJoinApply(query) { - return request({ - url: '/huayang/joinApply/list', - method: 'get', - params: query - }) -} - -export function getJoinApply(applyId) { - return request({ - url: '/huayang/joinApply/' + applyId, - method: 'get' - }) -} - -export function handleJoinApply(data) { - return request({ - url: '/huayang/joinApply/handle', - method: 'put', - data: data - }) -} - -export function deleteJoinApply(applyIds) { - return request({ - url: '/huayang/joinApply/' + applyIds, - method: 'delete' - }) -} \ No newline at end of file diff --git a/admin/src/api/huayang/news.js b/admin/src/api/huayang/news.js deleted file mode 100644 index 510a24c..0000000 --- a/admin/src/api/huayang/news.js +++ /dev/null @@ -1,47 +0,0 @@ -import request from '@/utils/request' - -export function listNews(query) { - return request({ - url: '/huayang/news/list', - method: 'get', - params: query - }) -} - -export function getNews(newsId) { - return request({ - url: '/huayang/news/' + newsId, - method: 'get' - }) -} - -export function addNews(data) { - return request({ - url: '/huayang/news', - method: 'post', - data: data - }) -} - -export function updateNews(data) { - return request({ - url: '/huayang/news', - method: 'put', - data: data - }) -} - -export function deleteNews(newsIds) { - return request({ - url: '/huayang/news/' + newsIds, - method: 'delete' - }) -} - -export function exportNews(query) { - return request({ - url: '/huayang/news/export', - method: 'get', - params: query - }) -} \ No newline at end of file diff --git a/admin/src/api/huayang/newsCategory.js b/admin/src/api/huayang/newsCategory.js deleted file mode 100644 index 3d0d791..0000000 --- a/admin/src/api/huayang/newsCategory.js +++ /dev/null @@ -1,39 +0,0 @@ -import request from '@/utils/request' - -export function listNewsCategory(query) { - return request({ - url: '/huayang/newsCategory/list', - method: 'get', - params: query - }) -} - -export function getNewsCategory(dictCode) { - return request({ - url: '/huayang/newsCategory/' + dictCode, - method: 'get' - }) -} - -export function addNewsCategory(data) { - return request({ - url: '/huayang/newsCategory', - method: 'post', - data: data - }) -} - -export function updateNewsCategory(data) { - return request({ - url: '/huayang/newsCategory', - method: 'put', - data: data - }) -} - -export function delNewsCategory(dictCode) { - return request({ - url: '/huayang/newsCategory/' + dictCode, - method: 'delete' - }) -} diff --git a/admin/src/api/huayang/newsPageBanner.js b/admin/src/api/huayang/newsPageBanner.js deleted file mode 100644 index feee737..0000000 --- a/admin/src/api/huayang/newsPageBanner.js +++ /dev/null @@ -1,16 +0,0 @@ -import request from '@/utils/request' - -export function getNewsPageBanner() { - return request({ - url: '/huayang/newsPage/banner', - method: 'get' - }) -} - -export function updateNewsPageBanner(data) { - return request({ - url: '/huayang/newsPage/banner', - method: 'put', - data - }) -} diff --git a/admin/src/api/huayang/translation.js b/admin/src/api/huayang/translation.js index 2d5b2fa..adbba66 100644 --- a/admin/src/api/huayang/translation.js +++ b/admin/src/api/huayang/translation.js @@ -1,32 +1,131 @@ import request from '@/utils/request' -export function listTranslation(query) { +/** + * 列出可翻译页面(左侧页面树) + */ +export function listTranslationPages() { + return request({ + url: '/huayang/translation/pages', + method: 'get' + }) +} + +/** + * 列出当前启用的目标语种(不含默认 zh_cn) + */ +export function listTargetLangs() { + return request({ + url: '/huayang/translation/targetLangs', + method: 'get' + }) +} + +/** + * 实时扫描某页面下的全部可翻译条目 + * @param {string} pageModule 页面模块标识,如 home / product / culture + */ +export function scanTranslation(pageModule) { + return request({ + url: '/huayang/translation/scan', + method: 'get', + params: { pageModule } + }) +} + +/** + * 批量保存翻译条目 + * @param {{ pageModule: string, items: Array<{langCode: string, key: string, value: string}> }} payload + * items[].value 为空字符串时表示删除该 (langCode, key) 记录 + */ +export function batchSaveTranslation(payload) { + return request({ + url: '/huayang/translation/batch', + method: 'put', + data: payload + }) +} + +/** + * 全局完成度统计 + */ +export function translationStats() { + return request({ + url: '/huayang/translation/stats', + method: 'get' + }) +} + +/** + * 获取翻译菜单树(用于手动选择显示菜单) + */ +export function getTranslationMenuOptions() { + return request({ + url: '/huayang/translation/menuOptions', + method: 'get' + }) +} + +/** + * 获取已选择菜单 ID 列表 + */ +export function getTranslationMenuSelection() { + return request({ + url: '/huayang/translation/menuSelection', + method: 'get' + }) +} + +/** + * 保存菜单选择 + * @param {{ menuIds: number[] }} payload + */ +export function saveTranslationMenuSelection(payload) { + return request({ + url: '/huayang/translation/menuSelection', + method: 'put', + data: payload + }) +} + +/** + * 查询翻译条目列表(走多语言国际化通用接口) + */ +export function listTranslationEntries(params) { return request({ url: '/huayang/translation/list', method: 'get', - params: query + params }) } -export function addTranslation(data) { +/** + * 新增翻译条目(按 key 维护静态文案) + */ +export function addTranslationEntry(data) { return request({ url: '/huayang/translation', method: 'post', - data: data + data }) } -export function updateTranslation(data) { +/** + * 修改翻译条目(按 key 维护静态文案) + */ +export function updateTranslationEntry(data) { return request({ url: '/huayang/translation', method: 'put', - data: data + data }) } -export function deleteTranslation(i18nIds) { +/** + * 删除翻译条目 + */ +export function deleteTranslationEntry(i18nIds) { return request({ - url: '/huayang/translation/' + i18nIds, + url: `/huayang/translation/${i18nIds}`, method: 'delete' }) } diff --git a/admin/src/components/Editor/index.vue b/admin/src/components/Editor/index.vue index ef32b57..af6a20c 100644 --- a/admin/src/components/Editor/index.vue +++ b/admin/src/components/Editor/index.vue @@ -33,7 +33,7 @@ append-to-body custom-class="editor-media-dialog" > -

请选择要插入的类型(新闻正文支持图片与视频混排)

+

请选择要插入的类型(正文支持图片与视频混排)

插入图片 插入视频(上传文件) @@ -104,7 +104,7 @@ export default { /* 上传文件大小限制(MB) */ fileSize: { type: Number, - default: 5, + default: null, }, /* 类型(base64格式、url格式) */ type: { @@ -326,14 +326,6 @@ export default { this.$message.error(`图片格式错误,请使用 jpg/png/gif/webp/svg`) return false } - // 校检文件大小 - if (this.fileSize) { - const isLt = file.size / 1024 / 1024 < this.fileSize - if (!isLt) { - this.$message.error(`上传文件大小不能超过 ${this.fileSize} MB!`) - return false - } - } return true }, handleUploadSuccess(res, file) { diff --git a/admin/src/components/ImageUpload/index.vue b/admin/src/components/ImageUpload/index.vue index 41161b0..b43e676 100644 --- a/admin/src/components/ImageUpload/index.vue +++ b/admin/src/components/ImageUpload/index.vue @@ -25,7 +25,6 @@
请上传 - 的文件
@@ -68,13 +67,13 @@ export default { }, // 大小限制(MB) fileSize: { - type: Number, - default: 5 + type: Number, + default: null }, // 文件类型, 例如['png', 'jpg', 'jpeg'] fileType: { type: Array, - default: () => ["png", "jpg", "jpeg"] + default: () => ["png", "jpg", "jpeg", "gif", "webp", "svg", "bmp"] }, // 是否显示提示 isShowTip: { @@ -90,6 +89,11 @@ export default { drag: { type: Boolean, default: true + }, + // 兼容历史页面传入的 spec,界面不再展示建议规格 + spec: { + type: String, + default: '' } }, data() { @@ -128,16 +132,7 @@ export default { // 首先将值转为数组 const list = Array.isArray(val) ? val : this.value.split(',') // 然后将数组转为对象数组 - this.fileList = list.map(item => { - if (typeof item === "string") { - if (item.indexOf(this.baseUrl) === -1 && !isExternal(item)) { - item = { name: this.baseUrl + item, url: this.baseUrl + item } - } else { - item = { name: item, url: item } - } - } - return item - }) + this.fileList = list.map(item => this.normalizeFileItem(item)) } else { this.fileList = [] return [] @@ -150,8 +145,8 @@ export default { computed: { // 是否显示提示 showTip() { - return this.isShowTip && (this.fileType || this.fileSize) - }, + return this.isShowTip && this.fileType + } }, methods: { // 上传前loading加载 @@ -179,15 +174,9 @@ export default { this.$modal.msgError('文件名不正确,不能包含英文逗号!') return false } - if (this.fileSize) { - const isLt = file.size / 1024 / 1024 < this.fileSize - if (!isLt) { - this.$modal.msgError(`上传头像图片大小不能超过 ${this.fileSize} MB!`) - return false - } - } this.$modal.loading("正在上传图片,请稍候...") this.number++ + return true }, // 文件个数超出 handleExceed() { @@ -197,7 +186,7 @@ export default { handleUploadSuccess(res, file) { if (res.code == 200) { const path = res.fileName || res.url || '' - this.uploadList.push({ name: path, url: path }) + this.uploadList.push(this.normalizeFileItem(path)) this.uploadedSuccessfully() } else { this.number-- @@ -244,10 +233,35 @@ export default { separator = separator || "," for (let i in list) { if (list[i].url) { - strs += list[i].url.replace(this.baseUrl, "") + separator + strs += this.getStorePath(list[i]) + separator } } return strs != '' ? strs.substr(0, strs.length - 1) : '' + }, + normalizeFileItem(item) { + if (typeof item === "string") { + return { name: item, url: this.getPreviewUrl(item) } + } + const path = item.url || item.name || "" + return { + ...item, + name: item.name || path, + url: this.getPreviewUrl(path) + } + }, + getPreviewUrl(path) { + if (!path || isExternal(path)) { + return path + } + if (path.indexOf(this.baseUrl) === 0) { + return path + } + const normalizedPath = path.charAt(0) === "/" ? path : `/${path}` + return this.baseUrl + normalizedPath + }, + getStorePath(file) { + const path = file.url || file.name || "" + return path.indexOf(this.baseUrl) === 0 ? path.replace(this.baseUrl, "") : path } } } @@ -272,5 +286,6 @@ export default { opacity: 0; transform: translateY(0); } + diff --git a/admin/src/i18n/index.js b/admin/src/i18n/index.js index b7a1c94..9454986 100644 --- a/admin/src/i18n/index.js +++ b/admin/src/i18n/index.js @@ -5,9 +5,13 @@ import messages from './lang' Vue.use(VueI18n) -const cookieLang = Cookies.get('language') || 'zh' +const cookieLangRaw = Cookies.get('language') || 'zh' +if (cookieLangRaw === 'ko') { + Cookies.set('language', 'zh') +} +const cookieLang = cookieLangRaw === 'ko' ? 'zh' : cookieLangRaw const i18n = new VueI18n({ - locale: cookieLang === 'ko' ? 'zh' : cookieLang, + locale: cookieLang, messages }) diff --git a/admin/src/layout/components/Navbar.vue b/admin/src/layout/components/Navbar.vue index ab7c6c7..c219c71 100644 --- a/admin/src/layout/components/Navbar.vue +++ b/admin/src/layout/components/Navbar.vue @@ -22,24 +22,6 @@ - - - - {{ languageName }} - - - - 简体中文 - - - English - - - 日本語 - - - - @@ -91,14 +73,7 @@ export default { HeaderNotice }, data() { - return { - locale: 'zh', - languageMap: { - zh: '简体中文', - en: 'English', - ja: '日本語' - } - } + return {} }, computed: { ...mapGetters([ @@ -121,9 +96,6 @@ export default { get() { return this.$store.state.settings.sidebarLogo } - }, - languageName() { - return this.languageMap[this.locale] || '简体中文' } }, methods: { @@ -149,12 +121,6 @@ export default { location.href = '/index' }) }).catch(() => {}) - }, - changeLanguage(lang) { - this.locale = lang - this.$i18n.locale = lang - Cookies.set('language', lang) - location.reload() } } } @@ -241,15 +207,6 @@ export default { } } - .language-dropdown { - display: flex; - align-items: center; - font-size: 14px; - i { - margin-right: 4px; - } - } - .avatar-container { margin-right: 0px; padding-right: 0px; @@ -285,9 +242,4 @@ export default { } } } - -.active-language { - color: #409EFF; - font-weight: 600; -} diff --git a/admin/src/views/huayang/companyHonor/index.vue b/admin/src/views/huayang/companyHonor/index.vue index 1dec193..a01353f 100644 --- a/admin/src/views/huayang/companyHonor/index.vue +++ b/admin/src/views/huayang/companyHonor/index.vue @@ -80,7 +80,6 @@ 无 -