refactor: 完成多语言国际化全量替换与补充

1. 新增大量中英文语言包文件,覆盖自定义组件、页面、工具类的文案
2. 替换硬编码文本为多语言调用,统一全局文案管理
3. 优化语言包结构,拆分公共模块与业务模块
4. 修复部分语言加载与国际化适配问题
This commit is contained in:
2026-06-26 17:32:35 +08:00
parent f7fb9f34f9
commit 4ae21c5d7a
91 changed files with 2650 additions and 281 deletions

View File

@@ -91,8 +91,8 @@
updateManager.onUpdateReady(function(res) {
uni.showModal({
title: '更新提示',
content: '新版本已经准备好,是否重启应用?',
title: Lang.lang('common.updateTip'),
content: Lang.lang('common.updateReady'),
success(res) {
if (res.confirm) {
// 新的版本已经下载好,调用 applyUpdate 应用新版本并重启
@@ -118,8 +118,8 @@
uni.onNetworkStatusChange(function(res) {
if (!res.isConnected) {
uni.showModal({
title: '网络失去链接',
content: '请检查网络链接',
title: Lang.lang('common.networkLost'),
content: Lang.lang('common.networkCheck'),
showCancel: false
});
}

View File

@@ -5,7 +5,7 @@
export const langConfig = {
// 语言列表 — 与后端 Lang.php::overrides() 白名单对齐
// 增删需要后端同步改 LangOverrideModel::normalizeAppType 旁的 $allow_lang
langList: ['zh-cn', 'en-us', 'zh-tw', 'ja-jp', 'ko-kr', 'th-th', 'vi-vn', 'id-id', 'es-es', 'fr-fr', 'de-de', 'ru-ru', 'ar-sa', 'pt-pt', 'it-it', 'tr-tr'],
langList: ['zh-cn', 'en-us'],
// 默认语言
defaultLang: 'zh-cn',
// 语言包加载方式:'lazy'(按需加载)或 'preload'(预加载)

View File

@@ -11,7 +11,13 @@ try {
uniacid: site.uniacid,
}
} else {
console.warn('小程序发行环境下,必须配置site.js文件, 不然将使用默认配置,影响小程序定制分发!');
// 惰性加载 Lang 避免循环依赖(lang.js 引用了 config.js)
try {
const Lang = require('@/common/js/lang.js').default;
console.warn(Lang.lang('common.config.siteJsNotConfigured'));
} catch (e) {
console.warn('小程序发行环境下,必须配置site.js文件,不然将使用默认配置,影响小程序定制分发!');
}
}
} catch (e) {}
// #endif

View File

@@ -100,7 +100,7 @@ export class CustomerService {
if (!config || !config.type) {
result.isValid = false;
result.errors.push('客服类型未配置');
result.errors.push(this.vm.$lang('customerService.serviceTypeNotConfigured'));
return result;
}

View File

@@ -58,6 +58,10 @@ export default {
isEnEnv() {
return this.$langConfig.getCurrentLocale() === 'en-us';
},
// 是否是非中文环境(用于多语言商品名/字段切换)
isNotZhEnv() {
return this.$langConfig.getCurrentLocale() !== 'zh-cn';
},
// 动态判断:当前页面是否是 TabBar 根页面(tab 根页不应显示返回按钮)
isTabRootPage() {
const tabBarList = this.$store.state.tabBarList;

View File

@@ -261,7 +261,7 @@ export class NavigationHelper {
}
// 显示用户友好的错误信息
this.showError(Lang.lang('navigation.serviceUnavailable'))
this.showError(Lang.lang('common.navigation.serviceUnavailable'))
}
// 显示错误提示

View File

@@ -149,28 +149,28 @@ export const Platform = {
// #ifdef H5
if (platform === 'h5') {
cachedAppTypeName = this.isWeiXin()
? _t('platform.wechatOfficialAccount', '微信公众号')
: _t('platform.h5', 'H5');
? _t('common.platform.wechatOfficialAccount', '微信公众号')
: _t('common.platform.h5', 'H5');
return cachedAppTypeName;
}
// #endif
const platformNameMap = {
weapp: _t('platform.wechatMiniProgram', '微信小程序'),
alipay: _t('platform.alipayMiniProgram', '支付宝小程序'),
baidu: _t('platform.baiduMiniProgram', '百度小程序'),
toutiao: _t('platform.toutiaoMiniProgram', '头条小程序'),
qq: _t('platform.qqMiniProgram', 'QQ小程序'),
kuaishou: _t('platform.kuaishouMiniProgram', '快手小程序'),
jd: _t('platform.jdMiniProgram', '京东小程序'),
'360': _t('platform.miniProgram360', '360小程序'),
xhs: _t('platform.xhsMiniProgram', '小红书小程序'),
harmony: _t('platform.harmonyService', '鸿蒙元服务'),
app: _t('platform.app', 'APP'),
pc: _t('platform.pc', 'PC端'),
weapp: _t('common.platform.wechatMiniProgram', '微信小程序'),
alipay: _t('common.platform.alipayMiniProgram', '支付宝小程序'),
baidu: _t('common.platform.baiduMiniProgram', '百度小程序'),
toutiao: _t('common.platform.toutiaoMiniProgram', '头条小程序'),
qq: _t('common.platform.qqMiniProgram', 'QQ小程序'),
kuaishou: _t('common.platform.kuaishouMiniProgram', '快手小程序'),
jd: _t('common.platform.jdMiniProgram', '京东小程序'),
'360': _t('common.platform.miniProgram360', '360小程序'),
xhs: _t('common.platform.xhsMiniProgram', '小红书小程序'),
harmony: _t('common.platform.harmonyService', '鸿蒙元服务'),
app: _t('common.platform.app', 'APP'),
pc: _t('common.platform.pc', 'PC端'),
};
cachedAppTypeName = platformNameMap[platform] || _t('platform.unknown', '未知平台');
cachedAppTypeName = platformNameMap[platform] || _t('common.platform.unknown', '未知平台');
return cachedAppTypeName;
},

View File

@@ -433,21 +433,46 @@ export default {
* @param {string} mode 模式 navigateTo、redirectTo、reLaunch、tabbar, 默认navigateTo
*/
redirectTo(to, param = {}, mode = 'navigateTo') {
let url = to;
// 1. 拆分 path 与已有 query,避免后面 indexOf('?') 误判
const queryIdx = to.indexOf('?');
let path = queryIdx === -1 ? to : to.substring(0, queryIdx);
const existingParams = {};
if (queryIdx !== -1) {
to.substring(queryIdx + 1).split('&').forEach(kv => {
if (!kv) return;
// 用 indexOf('=') 取首个等号,兼容 value 里带 '=' 的场景
const eqIdx = kv.indexOf('=');
const k = eqIdx === -1 ? kv : kv.substring(0, eqIdx);
const v = eqIdx === -1 ? '' : kv.substring(eqIdx + 1);
if (k) existingParams[k] = v;
});
}
// 2. 合并 param:同 key 时新值覆盖旧值,杜绝 ?name=A&name=B 这种重复
const merged = Object.assign({}, existingParams, param || {});
// 3. 统一编码。decode -> encode 双向 roundtrip 兼具两种语义:
// - 调用方传原始值(/pages_tool/index/diy?name=XXX) -> encode 后 %2F%3F...
// - 调用方已传编码值(%2F%3F...) -> decode 还原 -> encode 还原回去,不会被二次编码
// - 任何不能 decode 的非法序列(如裸 %) 走 catch 路径,直接 encode
const safeEncode = (v) => {
const s = v == null ? '' : String(v);
try {
return encodeURIComponent(decodeURIComponent(s));
} catch (e) {
return encodeURIComponent(s);
}
};
const queryStr = Object.keys(merged)
.map(k => safeEncode(k) + '=' + safeEncode(merged[k]))
.join('&');
let url = path + (queryStr ? '?' + queryStr : '');
// 替换url中的前缀
url = adaptSubpackageUrl(url);
if (param != undefined) {
Object.keys(param).forEach(function (key) {
if (url.indexOf('?') != -1) {
url += "&" + key + "=" + param[key];
} else {
url += "?" + key + "=" + param[key];
}
});
}
// 针对tabBar.list中的路径,直接切换tabBar
for (let i = 0; i < systemTabBarList.length; i++) {
const tabBarUrl = systemTabBarList[i];
@@ -897,7 +922,7 @@ export default {
}
if (!isObject(obj)) {
throw new Error('obj 不是一个对象!')
throw new Error(Lang.lang('util.deepCloneNotObject'))
}
//判断传进来的是对象还是数组
let isArray = Array.isArray(obj)
@@ -968,7 +993,7 @@ export default {
// #ifdef MP
this.redirectTo('/pages_tool/webview/webview', {
...link,
src: encodeURIComponent(url)
src: url
});
// #endif

View File

@@ -60,7 +60,7 @@
</view>
<view class="sale" v-if="goodsValue.saleStyle.control"
:style="{ color: goodsValue.theme == 'diy' ? goodsValue.saleStyle.color : '' }">
已售{{ item.sale_num }}{{ item.unit ? item.unit : '件' }}
{{ $lang('components-diy.diyGoodsList.soldCount', { count: item.sale_num, unit: item.unit || $lang('components-diy.diyGoodsList.soldUnit') }) }}
</view>
</view>
@@ -168,7 +168,7 @@
</view>
<view class="sale" v-if="goodsValue.saleStyle.control"
:style="{ color: goodsValue.theme == 'diy' ? goodsValue.saleStyle.color : '' }">
已售{{ item.sale_num }}{{ item.unit ? item.unit : '件' }}
{{ $lang('components-diy.diyGoodsList.soldCount', { count: item.sale_num, unit: item.unit || $lang('components-diy.diyGoodsList.soldUnit') }) }}
</view>
</view>
</view>
@@ -238,7 +238,7 @@
</view>
<view class="sale" v-if="goodsValue.saleStyle.control"
:style="{ color: goodsValue.theme == 'diy' ? goodsValue.saleStyle.color : '' }">
已售{{ dataItem.sale_num }}{{ dataItem.unit ? dataItem.unit : '件' }}
{{ $lang('components-diy.diyGoodsList.soldCount', { count: dataItem.sale_num, unit: dataItem.unit || $lang('components-diy.diyGoodsList.soldUnit') }) }}
</view>
</view>
</view>

View File

@@ -146,14 +146,14 @@
<text class="unit">{{ $lang('common.currencySymbol') }}</text>
<text>{{ showMarketPrice(item) }}</text>
</view>
<view class="sale font-size-activity-tag color-tip">已售{{
item.sale_num }}{{ item.unit ? item.unit : '件' }}</view>
<view class="sale font-size-activity-tag color-tip">{{
$lang('components-diy.diyIndexPage.soldCount', { count: item.sale_num, unit: item.unit || $lang('components-diy.diyIndexPage.soldUnit') }) }}</view>
</view>
</view>
</view>
</view>
<view v-if="!isloading && goodsList[pageIndex].list.length == 0">
<ns-empty text="该分类下暂无商品" :isIndex="false"></ns-empty>
<ns-empty :text="$lang('components-diy.diyIndexPage.noGoodsInCategory')" :isIndex="false"></ns-empty>
</view>
</block>
</mescroll-uni>
@@ -193,7 +193,7 @@ export default {
pageIndex: 0, //当前选中分类id
cateList: [{
//header分类
category_name: '首页'
category_name: ''
}],
twoCategorylist: [], //二级分类
twoCategorylistId: 0, //二级分类所在的swiper
@@ -258,6 +258,9 @@ export default {
return data;
}
},
created() {
this.cateList[0].category_name = this.$lang('components-diy.diyIndexPage.home');
},
watch: {
type(newVal, oldVal) {
if (newVal) {
@@ -323,7 +326,7 @@ export default {
let obj = {
list: []
};
obj.category_name = this.value.title ? this.value.title : '首页';
obj.category_name = this.value.title ? this.value.title : this.$lang('components-diy.diyIndexPage.home');
arr.push(obj);
this.cateList = arr.concat(res.data);
Object.keys(this.cateList).forEach((key, index) => {

View File

@@ -42,11 +42,11 @@
<uni-popup ref="noticePopup" type="center">
<view class="notice-popup">
<view class="head-wrap" @tap.stop="closeNoticePopup">
<text>公告</text>
<text>{{ $lang('components-diy.diyNotice.notice') }}</text>
<text class="iconfont icon-close"></text>
</view>
<view class="content-wrap">{{ notice }}</view>
<button type="primary" @tap.stop="closeNoticePopup">我知道了</button>
<button type="primary" @tap.stop="closeNoticePopup">{{ $lang('components-diy.diyNotice.gotIt') }}</button>
</view>
</uni-popup>
</view>

View File

@@ -143,7 +143,7 @@ export default {
});
video.addEventListener('error', (e) => {
console.error('H5端预加载视频失败:', e);
console.error(this.$lang('components-diy.diyVideo.h5PreloadFailed') + ':', e);
});
// 触发加载
@@ -184,7 +184,7 @@ export default {
// 视频错误事件
onVideoError(event) {
console.error('视频加载失败:', event);
console.error(this.$lang('components-diy.diyVideo.videoLoadFailed') + ':', event);
},
async handlerClick(videoUrl) {

View File

@@ -1,35 +1,37 @@
// 全局配置
// mescroll-body 和 mescroll-uni 通用
const GlobalOption = {
down: {
// 其他down的配置参数也可以写,这里只展示了常用的配置:
textInOffset: '下拉刷新', // 下拉的距离在offset范围内的提示文本
textOutOffset: '释放更新', // 下拉的距离大于offset范围的提示文本
textLoading: '加载中 ...', // 加载中的提示文本
offset: 80, // 在列表顶部,下拉大于80px,松手即可触发下拉刷新的回调
native: false // 是否使用系统自带的下拉刷新; 默认false; 仅在mescroll-body生效 (值为true时,还需在pages配置enablePullDownRefresh:true;详请参考mescroll-native的案例)
},
up: {
// 其他up的配置参数也可以写,这里只展示了常用的配置:
textLoading: '加载中 ...', // 加载中的提示文本
textNoMore: '', // 没有更多数据的提示文本
// textNoMore: '— 我是有底线的 —', // 没有更多数据的提示文本
offset: 80, // 距底部多远时,触发upCallback
isBounce: false, // 默认禁止橡皮筋的回弹效果, 必读事项: http://www.mescroll.com/qa.html?v=190725#q25
toTop: {
// 回到顶部按钮,需配置src才显示
src: "https://www.mescroll.com/img/mescroll-totop.png?v=1", // 图片路径 (建议放入static目录, 如 /static/img/mescroll-totop.png )
offset: 1000, // 列表滚动多少距离才显示回到顶部按钮,默认1000px
right: 20, // 到右边的距离, 默认20 (支持"20rpx", "20px", "20%"格式的值, 纯数字则默认单位rpx)
bottom: 120, // 到底部的距离, 默认120 (支持"20rpx", "20px", "20%"格式的值, 纯数字则默认单位rpx)
width: 72 // 回到顶部图标的宽度, 默认72 (支持"20rpx", "20px", "20%"格式的值, 纯数字则默认单位rpx)
},
empty: {
use: true, // 是否显示空布局
icon: "https://www.mescroll.com/img/mescroll-empty.png?v=1", // 图标路径 (建议放入static目录, 如 /static/img/mescroll-empty.png )
tip: '~ 暂无相关数据 ~' // 提示
}
}
}
export default GlobalOption
// 全局配置
// mescroll-body 和 mescroll-uni 通用
import Lang from '@/common/js/lang.js';
const GlobalOption = {
down: {
// 其他down的配置参数也可以写,这里只展示了常用的配置:
textInOffset: Lang.lang('components.mescroll.down.textInOffset', '下拉刷新'), // 下拉的距离在offset范围内的提示文本
textOutOffset: Lang.lang('components.mescroll.down.textOutOffset', '释放更新'), // 下拉的距离大于offset范围的提示文本
textLoading: Lang.lang('components.mescroll.down.textLoading', '加载中 ...'), // 加载中的提示文本
offset: 80, // 在列表顶部,下拉大于80px,松手即可触发下拉刷新的回调
native: false // 是否使用系统自带的下拉刷新; 默认false; 仅在mescroll-body生效 (值为true时,还需在pages配置enablePullDownRefresh:true;详请参考mescroll-native的案例)
},
up: {
// 其他up的配置参数也可以写,这里只展示了常用的配置:
textLoading: Lang.lang('components.mescroll.up.textLoading', '加载中 ...'), // 加载中的提示文本
textNoMore: '', // 没有更多数据的提示文本
// textNoMore: '— 我是有底线的 —', // 没有更多数据的提示文本
offset: 80, // 距底部多远时,触发upCallback
isBounce: false, // 默认禁止橡皮筋的回弹效果, 必读事项: http://www.mescroll.com/qa.html?v=190725#q25
toTop: {
// 回到顶部按钮,需配置src才显示
src: "https://www.mescroll.com/img/mescroll-totop.png?v=1", // 图片路径 (建议放入static目录, 如 /static/img/mescroll-totop.png )
offset: 1000, // 列表滚动多少距离才显示回到顶部按钮,默认1000px
right: 20, // 到右边的距离, 默认20 (支持"20rpx", "20px", "20%"格式的值, 纯数字则默认单位rpx)
bottom: 120, // 到底部的距离, 默认120 (支持"20rpx", "20px", "20%"格式的值, 纯数字则默认单位rpx)
width: 72 // 回到顶部图标的宽度, 默认72 (支持"20rpx", "20px", "20%"格式的值, 纯数字则默认单位rpx)
},
empty: {
use: true, // 是否显示空布局
icon: "https://www.mescroll.com/img/mescroll-empty.png?v=1", // 图标路径 (建议放入static目录, 如 /static/img/mescroll-empty.png )
tip: Lang.lang('components.mescroll.empty.tip', '~ 暂无相关数据 ~') // 提示
}
}
}
export default GlobalOption

View File

@@ -4,6 +4,18 @@
* http://www.mescroll.com
*/
import Lang from '@/common/js/lang.js';
// 国际化文本获取:优先从语言包读取,失败则使用默认值(向后兼容)
function __i18n(key, defaultVal) {
try {
var val = Lang.lang(key, defaultVal);
return val || defaultVal;
} catch (e) {
return defaultVal;
}
}
export default function MeScroll(options, isScrollBody) {
let me = this;
me.version = '1.2.3'; // mescroll版本号
@@ -52,9 +64,9 @@ MeScroll.prototype.extendDownScroll = function(optDown) {
outOffsetRate: 0.2, // 在列表顶部,下拉的距离大于offset时,改变下拉区域高度比例;值小于1且越接近0,高度变化越小,表现为越往下越难拉
bottomOffset: 20, // 当手指touchmove位置在距离body底部20px范围内的时候结束上拉刷新,避免Webview嵌套导致touchend事件不执行
minAngle: 45, // 向下滑动最少偏移的角度,取值区间 [0,90];默认45度,即向下滑动的角度大于45度则触发下拉;而小于45度,将不触发下拉,避免与左右滑动的轮播等组件冲突;
textInOffset: '下拉刷新', // 下拉的距离在offset范围内的提示文本
textOutOffset: '释放更新', // 下拉的距离大于offset范围的提示文本
textLoading: '加载中 ...', // 加载中的提示文本
textInOffset: __i18n('components.mescroll.down.textInOffset', '下拉刷新'), // 下拉的距离在offset范围内的提示文本
textOutOffset: __i18n('components.mescroll.down.textOutOffset', '释放更新'), // 下拉的距离大于offset范围的提示文本
textLoading: __i18n('components.mescroll.down.textLoading', '加载中 ...'), // 加载中的提示文本
inited: null, // 下拉刷新初始化完毕的回调
inOffset: null, // 下拉的距离进入offset范围内那一刻的回调
outOffset: null, // 下拉的距离大于offset那一刻的回调
@@ -87,7 +99,7 @@ MeScroll.prototype.extendUpScroll = function(optUp) {
},
noMoreSize: 5, // 如果列表已无数据,可设置列表的总数量要大于等于5条才显示无更多数据;避免列表数据过少(比如只有一条数据),显示无更多数据会不好看
offset: 80, // 距底部多远时,触发upCallback
textLoading: '加载中 ...', // 加载中的提示文本
textLoading: __i18n('components.mescroll.up.textLoading', '加载中 ...'), // 加载中的提示文本
textNoMore: '-- END --', // 没有更多数据的提示文本
inited: null, // 初始化完毕的回调
showLoading: null, // 显示加载中的回调
@@ -112,7 +124,7 @@ MeScroll.prototype.extendUpScroll = function(optUp) {
empty: {
use: true, // 是否显示空布局
icon: null, // 图标路径
tip: '~ 暂无相关数据 ~', // 提示
tip: __i18n('components.mescroll.empty.tip', '~ 暂无相关数据 ~'), // 提示
btnText: '', // 按钮
btnClick: null, // 点击按钮的回调
onShow: null, // 是否显示的回调

View File

@@ -198,6 +198,23 @@
? this.undisableText
: this.$lang('components.yuyueDate.available');
},
// 周几名称数组(索引 0-6 对应周日到周六,与 Date.getDay() 返回值一致)
// 用于显示层(Toast 等),会随语言切换
weekBox() {
return [
this.$lang('components.yuyueDate.weekSunday'),
this.$lang('components.yuyueDate.weekMonday'),
this.$lang('components.yuyueDate.weekTuesday'),
this.$lang('components.yuyueDate.weekWednesday'),
this.$lang('components.yuyueDate.weekThursday'),
this.$lang('components.yuyueDate.weekFriday'),
this.$lang('components.yuyueDate.weekSaturday'),
];
},
// 中文周几名称数组(用于与 disableWeek prop 做数据匹配,保持兼容性)
weekBoxCn() {
return ['周日', '周一', '周二', '周三', '周四', '周五', '周六'];
},
},
created(props) {
this.selectDate = this.nowDate = currentTime().date;
@@ -221,11 +238,10 @@
let date = e.split("-");
date = date[1] + "-" + date[2];
if (this.disableWeek.length && date >= weekDate()[0] && date <= weekDate()[1]) {
let weekBox = ['周日', '周一', '周二', '周三', '周四', '周五', '周六'];
let index = new Date(e).getDay();
if (this.disableWeek.indexOf(weekBox[index]) != -1) {
let dayIndex = new Date(e).getDay();
if (this.disableWeek.indexOf(this.weekBoxCn[dayIndex]) != -1) {
uni.showToast({
title: this.$lang('components.yuyueDate.weekdayCannotBook', { weekday: weekBox[index] }),
title: this.$lang('components.yuyueDate.weekdayCannotBook', { weekday: this.weekBox[dayIndex] }),
icon: 'none'
})
return false;
@@ -243,10 +259,9 @@
// 判断默认是不能选择的周,则都禁止选中
if (this.disableWeek.length && this.selectDate >= weekDate()[0] && this.selectDate <=
weekDate()[1]) {
let weekBox = ['周日', '周一', '周二', '周三', '周四', '周五', '周六'];
let date = currentTime().year + '-' + this.selectDate;
let index = new Date(date).getDay();
if (this.disableWeek.indexOf(weekBox[index]) != -1) {
let dayIndex = new Date(date).getDay();
if (this.disableWeek.indexOf(this.weekBoxCn[dayIndex]) != -1) {
item.disable = true;
}
}
@@ -309,11 +324,10 @@
// 日期选择事件
selectDateEvent(index, item) {
if (this.disableWeek.length && item.date >= weekDate()[0] && item.date <= weekDate()[1]) {
let weekBox = ['周日', '周一', '周二', '周三', '周四', '周五', '周六'];
let index = new Date(item.timeStamp).getDay();
if (this.disableWeek.indexOf(weekBox[index]) != -1) {
let dayIndex = new Date(item.timeStamp).getDay();
if (this.disableWeek.indexOf(this.weekBoxCn[dayIndex]) != -1) {
uni.showToast({
title: this.$lang('components.yuyueDate.weekdayCannotBook', { weekday: weekBox[index] }),
title: this.$lang('components.yuyueDate.weekdayCannotBook', { weekday: this.weekBox[dayIndex] }),
icon: 'none'
})
return false;

View File

@@ -42,6 +42,14 @@ export const lang = {
loadHistoryFail: 'Failed to load history',
about: '0B',
// ====== ai-chat-message - Auto Reply Templates ======
autoReplyGreeting: 'Hello! I am the AI customer service assistant, glad to serve you! How can I help you?',
autoReplyPrice: 'Our product prices vary by model and configuration. Please check the product detail page for specific prices.',
autoReplyShipping: 'Generally, orders are shipped within 24 hours. Please check the logistics information for specific timelines.',
autoReplyReturn: '7-day no-reason return is supported. Please ensure the product is in good condition.',
autoReplyService: 'Our customer service hours are 9:00-18:00. Feel free to contact us if you have any questions.',
autoReplyDefault: 'Thank you for your inquiry! Regarding your question about "{question}", our professional customer service will answer you as soon as possible. You can also check our help documentation for more information.',
// ====== ai-chat-search ======
searchEntrance: 'Search',
searchPlaceholder: 'Search chat history',
@@ -118,5 +126,14 @@ export const lang = {
menuClearChat: 'Clear Chat',
menuExport: 'Export Records',
menuSettings: 'Settings',
menuHelp: 'Help'
menuHelp: 'Help',
// ====== index.vue - Console Logs (for debugging) ======
logOnShowTriggered: '[AI Chat Page] onShow triggered',
logOnUnloadTriggered: '[AI Chat Page] onUnload triggered',
logKeyboardChangeUnavailable: 'onKeyboardHeightChange unavailable',
logEventHandleError: 'Event handling error',
logInitNavbarFailed: 'Failed to initialize navigation bar',
logChatComponentNotInit: 'Chat component not initialized',
logServicerConfigNotLoaded: '[AI Customer Service] servicerConfig not loaded, retrying after 200ms...'
}

View File

@@ -1,4 +1,10 @@
export const lang = {
//title为每个页面的标题
title: 'Article Details'
}
title: 'Article Details',
// publish time label
publishTime: 'Published:',
// read count label
readCount: 'Views: ',
// liked suffix (count + people liked)
likedSuffix: ' likes'
}

View File

@@ -1,5 +1,5 @@
export const lang = {
//title为每个页面的标题
//title is the title of each page
title: 'Article List',
emptyText: "No article information available currently"
}
emptyText: 'No articles'
}

View File

@@ -18,7 +18,40 @@ export const lang = {
goodsRecommendTitle: 'Guess you like',
currencySymbol: '¥',
submit: 'Submit',
searchTip: 'Please enter search keywords'
searchTip: 'Please enter search keywords',
// version update
updateTip: 'Update Available',
updateReady: 'A new version is ready, restart the app now?',
// network status
networkLost: 'Network Disconnected',
networkCheck: 'Please check your network connection',
// config related
config: {
siteJsNotConfigured: 'In the mini-program release environment, the site.js file must be configured, otherwise the default configuration will be used, which affects mini-program custom distribution!'
},
// platform detection
platform: {
wechatOfficialAccount: 'WeChat Official Account',
wechatMiniProgram: 'WeChat Mini Program',
alipayMiniProgram: 'Alipay Mini Program',
baiduMiniProgram: 'Baidu Mini Program',
toutiaoMiniProgram: 'Toutiao Mini Program',
qqMiniProgram: 'QQ Mini Program',
kuaishouMiniProgram: 'Kuaishou Mini Program',
jdMiniProgram: 'JD Mini Program',
miniProgram360: '360 Mini Program',
xhsMiniProgram: 'Xiaohongshu Mini Program',
harmonyService: 'Harmony Service',
app: 'APP',
pc: 'PC',
h5: 'H5',
unknown: 'Unknown Platform',
},
},
// navigation utility
navigation: {
serviceUnavailable: 'Navigation service temporarily unavailable',
},
// diy custom page
@@ -58,6 +91,7 @@ export const lang = {
serviceUnavailable: 'Customer service temporarily unavailable',
wxworkLinkNotConfigured: 'WeChat Work link not configured',
fromFloatButton: 'Inquiry from floating button',
serviceTypeNotConfigured: 'Customer service type not configured',
},
// WeChat Work JS-SDK
@@ -153,6 +187,8 @@ export const lang = {
weekThursday: 'Thu',
weekFriday: 'Fri',
weekSaturday: 'Sat',
// error messages
deepCloneNotObject: 'obj is not an object!',
},
// AI service
@@ -215,11 +251,6 @@ export const lang = {
logoutTitle: 'Tip',
logoutContent: 'Are you sure you want to log out?',
// navigation utility
navigation: {
serviceUnavailable: 'Navigation service temporarily unavailable',
},
// WeChat Map JSSDK
mapWxJssdk: {
// error messages

View File

@@ -1,7 +1,16 @@
export const lang = {
expand: 'Expand',
clickToCollapse: 'Click to collapse',
reachedBottom: 'Reached the bottom~',
noCategory: 'No categories yet!',
pieceUnit: 'pc(s)',
'components-diy': {
diyCategory: {
// Expand all categories (button on the right of horizontal scroll)
expand: 'Expand',
// Click to collapse (button at the bottom of expanded popup)
clickToCollapse: 'Click to collapse',
// Scroll to bottom tip
reachedBottom: 'Reached the bottom~',
// No categories (empty state tip)
noCategory: 'No categories yet!',
// Product unit: piece
pieceUnit: 'pc(s)',
},
},
}

View File

@@ -0,0 +1,7 @@
export const lang = {
// Sales text: Sold X units
soldCount: 'Sold {count}{unit}',
// Sales unit: piece
soldUnit: 'pcs',
}

View File

@@ -0,0 +1,14 @@
export const lang = {
'components-diy': {
diyIndexPage: {
// Home (default category name)
home: 'Home',
// Sales text: Sold X units
soldCount: 'Sold {count}{unit}',
// Sales unit: piece
soldUnit: 'pcs',
// Empty state prompt when no goods in category
noGoodsInCategory: 'No goods in this category',
},
},
}

View File

@@ -0,0 +1,4 @@
export const lang = {
notice: 'Notice',
gotIt: 'Got it',
}

View File

@@ -1,6 +1,14 @@
export const lang = {
switchStore: 'Switch',
locating: 'Locating...',
gettingLocation: 'Getting current location...',
searchPlaceholder: 'Search products',
}
'components-diy': {
diyStore: {
// Switch store button text
switchStore: 'Switch Store',
// Locating status text
locating: 'Locating',
// Getting location status text for address
gettingLocation: 'Getting location',
// Search input placeholder
searchPlaceholder: 'Search',
},
},
};

View File

@@ -0,0 +1,6 @@
export const lang = {
// H5 video preload failed
h5PreloadFailed: 'H5 video preload failed',
// Video load failed
videoLoadFailed: 'Video load failed',
}

View File

@@ -0,0 +1,22 @@
export const lang = {
// mescroll pull-down refresh / pull-up load component
components: {
mescroll: {
// pull-down refresh text
down: {
textInOffset: 'Pull to refresh',
textOutOffset: 'Release to update',
textLoading: 'Loading ...',
},
// pull-up load text
up: {
textLoading: 'Loading ...',
textNoMore: '-- END --',
},
// empty layout tip
empty: {
tip: '~ No data ~',
},
},
},
}

View File

@@ -1,4 +1,18 @@
export const lang = {
//title为每个页面的标题
title: 'Distributors',
// Member info
joinTime: 'Join Date: ',
// Stats units
peopleUnit: ' people',
orderUnit: ' orders',
currencyUnit: ' Yuan',
// Empty state
noData: 'No data',
// Toast messages
merchantNotOpenFenxiao: 'Merchant has not enabled distribution',
}

View File

@@ -1,4 +1,9 @@
export const lang = {
//title为每个页面的标题
title: 'View Files',
searchPlaceholder: 'Please enter the file name you want to search',
viewPdf: 'View PDF File',
emptyText: 'No files yet',
loading: 'Loading',
fileNotExist: 'File does not exist',
}

15
lang/en-us/form/form.js Normal file
View File

@@ -0,0 +1,15 @@
export const lang = {
// Form page
form: {
form: {
// Page title
title: 'Please fill in the form information',
// Submit success message
submitSuccess: 'Submitted successfully',
// Form information not available
noFormInfo: 'Form information not available',
},
},
}

View File

@@ -0,0 +1,48 @@
export const lang = {
// title is the page title
title: 'Bind Mobile',
bindMobile: {
// One-click authorization binding
oneClickAuthBind: 'One-Click Authorization Binding',
// Manual binding
manualBind: 'Manual Binding',
// Phone number
phoneNumber: 'Phone Number',
// Please enter phone number
pleInputMobile: 'Please enter phone number',
// Please enter a valid phone number
surePhoneNumber: 'Please enter a valid phone number',
// Verification code
confirmCode: 'Verification Code',
// Please enter verification code
confirmCodeInput: 'Please enter verification code',
// SMS verification code
smsCode: 'SMS Verification Code',
// Please enter dynamic code
animateCodeInput: 'Please enter dynamic code',
// Get dynamic code
findanimateCode: 'Get Dynamic Code',
// Code sent
codeSent: 'Sent ({seconds}s)',
// Do not click repeatedly
doNotRepeatedlyClick: 'Do not click repeatedly',
// Save / Bind
save: 'Bind',
// Binding successful
bindSuccess: 'Binding Successful',
// Same as original phone number, no need to modify
alikePhone: 'Same as original phone number, no need to modify',
// Tip text
tipTitle: 'Notice',
// Enjoy more services after binding your mobile number
bindBenefitTip: 'Enjoy more services after binding your mobile number',
// Send code tip
sendCodeTip: 'Click "Get Dynamic Code" and a verification code will be sent to your mobile number {mobile}',
},
}

View File

@@ -1,3 +1,6 @@
export const lang = {
title: 'Account Cancellation',
}
agreeTermsText: 'Check to indicate you have read and agree to this agreement',
nextStep: 'Next',
pleaseAgreeFirst: 'Please check to agree to the agreement first',
}

View File

@@ -0,0 +1,25 @@
export const lang = {
//title is the title of each page
title: 'Membership Card Log',
// Stats header
totalConsume: 'Total Consume',
cardBalance: 'Balance',
totalRecharge: 'Total Recharge',
yuanSuffix: ' Yuan',
statUnit: ' ({unit})',
// Filter tabs
filterAll: 'All',
filterIncome: 'Income',
filterExpense: 'Expense',
filterRecharge: 'Recharge',
filterConsume: 'Consume',
filterGift: 'Gift',
// Empty state
emptyTips: 'No membership card records',
// Amount format
amountWithUnit: '{amount} Yuan',
}

View File

@@ -1,4 +1,7 @@
export const lang = {
//title为每个页面的标题
title: 'My Favorites'
}
title: 'My Favorites',
emptyText: 'No favorite products yet',
emptySubText: 'Go shopping and follow products you like!',
deleteSuccess: 'Deleted successfully'
}

View File

@@ -1,4 +1,9 @@
export const lang = {
//title为每个页面的标题
title: 'Modify Avatar'
}
title: 'Modify Avatar',
clickToUpload: 'Click to Upload',
confirmSave: 'Confirm Save',
reupload: 'Reupload',
uploadFailed: 'Avatar upload failed',
modifySuccess: 'Avatar modified successfully'
}

View File

@@ -1,4 +1,5 @@
export const lang = {
//title为每个页面的标题
title: 'Withdrawal Records'
title: 'Withdrawal Records',
emptyTips: 'No withdrawal records'
}

View File

@@ -1,7 +1,38 @@
export const lang = {
// title is the title of each page
title: 'Product List',
// Search box placeholder
searchPlaceholder: 'Please enter the product you want to search for',
// Default button text
buyText: 'Buy',
// Sort options
sort: {
comprehensive: 'Comprehensive',
sales: 'Sales',
price: 'Price',
filter: 'Filter',
},
// Goods list
goods: {
consultNow: 'Consult Now',
sold: 'Sold {num}{unit}',
defaultUnit: 'pcs',
noGoods: 'No products',
},
// Filter drawer
filter: {
title: 'Filter',
allCategories: 'All Categories',
all: 'All',
reset: 'Reset',
confirm: 'Confirm',
},
// Modal dialog
modal: {
title: 'Notice',

View File

@@ -15,5 +15,10 @@ export const lang = {
// Toast messages
toast: {
cancelSuccess: 'Cancelled successfully'
},
// Cancel refund modal
cancelRefund: {
content: 'After cancellation, this application will be closed. You can reapply if needed.',
cancelText: 'Not Now'
}
}

View File

@@ -0,0 +1,91 @@
export const lang = {
//title for each page
title: 'Invoice Application',
// ====== Header ======
header: {
title: 'Invoice Information',
desc: 'Please fill in the invoice information'
},
// ====== Invoice Type ======
invoiceType: {
title: 'Invoice Type',
paper: 'Paper Invoice',
paperDesc: 'Invoice will be mailed to the specified address',
electronic: 'Electronic Invoice',
electronicDesc: 'Invoice will be sent to your email'
},
// ====== Invoice Title ======
invoiceTitle: {
title: 'Invoice Title',
personal: 'Personal',
company: 'Company',
label: 'Invoice Title',
personalPlaceholder: 'Please enter your name',
companyPlaceholder: 'Please enter company name',
taxpayerId: 'Taxpayer ID',
taxpayerIdPlaceholder: 'Please enter taxpayer ID'
},
// ====== Invoice Content ======
invoiceContent: {
title: 'Invoice Content',
label: 'Invoice Content',
placeholder: 'Please select invoice content'
},
// ====== Mail Info (Paper Invoice) ======
mailInfo: {
title: 'Mailing Information',
receiver: 'Recipient',
receiverPlaceholder: 'Please enter recipient name',
mobile: 'Contact Phone',
mobilePlaceholder: 'Please enter phone number',
address: 'Mailing Address',
regionPlaceholder: 'Please select region',
detailAddress: 'Detailed Address',
detailAddressPlaceholder: 'Please enter detailed address'
},
// ====== Email Info (Electronic Invoice) ======
emailInfo: {
title: 'Email Information',
receiverEmail: 'Receiver Email',
emailPlaceholder: 'Please enter the email address to receive the invoice'
},
// ====== Fee Section ======
fee: {
taxFee: 'Invoice Tax',
deliveryFee: 'Delivery Fee'
},
// ====== Footer Submit Section ======
footer: {
tip: 'Tip: Invoice applications will be processed within {days} working days',
submit: 'Submit Application'
},
// ====== Form Validation Messages ======
validation: {
pleaseEnterTitle: 'Please enter invoice title',
pleaseEnterTaxpayerId: 'Please enter taxpayer ID',
pleaseSelectContent: 'Please select invoice content',
pleaseEnterReceiver: 'Please enter recipient name',
pleaseEnterMobile: 'Please enter phone number',
pleaseSelectRegion: 'Please select region',
pleaseEnterAddress: 'Please enter detailed address',
pleaseEnterEmail: 'Please enter receiver email',
invalidEmail: 'Please enter a valid email address'
},
// ====== API Messages ======
api: {
getConfigFailed: 'Failed to get invoice configuration',
submitSuccess: 'Invoice application submitted successfully',
submitFailed: 'Submission failed, please try again later',
networkError: 'Network error, please try again later'
}
}

View File

@@ -1,4 +1,16 @@
export const lang = {
//title为每个页面的标题
title: 'Logistics Information'
}
//title is the title of each page
title: 'Logistics Information',
// Order logistics info
order: {
logistics: {
// Express company info
expressCompany: 'Carrier: ',
trackingNo: 'Tracking No.: ',
// Toast messages
orderInfoNotFound: 'Order information not found!'
}
}
}

View File

@@ -64,6 +64,12 @@ export const lang = {
cancelSuccess: 'Cancelled successfully'
},
// ====== Cancel Refund Modal ======
cancelRefund: {
content: 'After cancellation, this application will be closed. You can reapply if needed.',
cancelText: 'Not Now'
},
// ====== Form Validation ======
validate: {
pleaseInputExpressCompany: 'Please enter logistics company',

View File

@@ -1,17 +1,27 @@
export const lang = {
// page title
title: 'Search Results',
// action buttons
exportImage: 'Export Image',
exportPdf: 'Export PDF',
// Seal detail
seal: {
medium: {
detail: {
// Page title
title: 'Seal Details',
// Export image
exportImage: 'Export Image',
// Export PDF
exportPdf: 'Export PDF',
// Loading
loading: 'Loading',
// File does not exist
fileNotExist: 'File does not exist',
// Save success
saveSuccess: 'Saved successfully',
// Save failed
saveFailed: 'Save failed',
// Download failed
downloadFailed: 'Download failed',
}
}
}
// loading tips
loading: 'Loading',
// toast messages
fileNotExist: 'File does not exist',
saveSuccess: 'Saved successfully',
saveFailed: 'Save failed, please try again later',
downloadFailed: 'Download failed',
}

View File

@@ -1,23 +1,23 @@
export const lang = {
//title为每个页面的标题
title: 'Material Search',
// Seal search page
seal: {
medium: {
search: {
// Search input placeholder
placeholder: "Enter medium name e.g. 'gasoline'",
placeholder: 'Enter keywords to search',
// Search results title
searchResult: 'Search Results',
// Empty state text
// Empty data tip
noData: 'No Data',
// Modal title
tip: 'Tip',
// Delete all history confirmation
confirmDeleteAllHistory: 'Confirm delete all history?',
// Delete single history record confirmation
confirmDeleteHistory: 'Confirm delete this history record?',
// Confirm delete all history
confirmDeleteAllHistory: 'Are you sure you want to delete all search history?',
// Confirm delete single history item
confirmDeleteHistory: 'Are you sure you want to delete this search history?',
}
}
}
}

View File

@@ -42,6 +42,14 @@ export const lang = {
loadHistoryFail: '加载历史失败',
about: '0B',
// ====== ai-chat-message - 自动回复模板 ======
autoReplyGreeting: '您好!我是AI智能客服,很高兴为您服务!有什么可以帮助您的吗?',
autoReplyPrice: '我们的产品价格根据型号和配置有所不同,具体价格请查看商品详情页面。',
autoReplyShipping: '一般情况下,订单会在24小时内发货,具体时效请查看物流信息。',
autoReplyReturn: '支持7天无理由退货,请确保商品完好无损。',
autoReplyService: '我们的客服工作时间是9:00-18:00,有问题可以随时联系我们。',
autoReplyDefault: '感谢您的咨询!关于"{question}"的问题,我们的专业客服会尽快为您解答。您也可以查看我们的帮助文档获取更多信息。',
// ====== ai-chat-search ======
searchEntrance: '搜索',
searchPlaceholder: '搜索聊天记录',
@@ -118,5 +126,14 @@ export const lang = {
menuClearChat: '清空聊天',
menuExport: '导出记录',
menuSettings: '设置',
menuHelp: '帮助'
menuHelp: '帮助',
// ====== index.vue - 控制台日志(调试用) ======
logOnShowTriggered: '[AI Chat Page] onShow 触发',
logOnUnloadTriggered: '[AI Chat Page] onUnload 触发',
logKeyboardChangeUnavailable: 'onKeyboardHeightChange 不可用',
logEventHandleError: '事件处理错误',
logInitNavbarFailed: '初始化导航栏失败',
logChatComponentNotInit: '聊天组件未初始化',
logServicerConfigNotLoaded: '【AI客服】servicerConfig 未加载,200ms后重试...'
}

View File

@@ -1,4 +1,10 @@
export const lang = {
//title为每个页面的标题
title: '文章详情'
title: '文章详情',
// 发表时间标签
publishTime: '发表时间:',
// 阅读数标签
readCount: '阅读:',
// 点赞数后缀(人数+人已赞)
likedSuffix: '人已赞'
}

View File

@@ -1,5 +1,5 @@
export const lang = {
//title为每个页面的标题
title: '文章列表',
emptyText:"当前暂无文章信息"
emptyText: '暂无文章'
}

View File

@@ -8,19 +8,52 @@ export const lang = {
member: '个人中心'
},
//公共
common: {
//语言包名称
name: '中文',
mescrollTextInOffset: '下拉刷新',
mescrollTextOutOffset: '释放更新',
mescrollEmpty: "暂无相关数据",
goodsRecommendTitle: '猜你喜欢',
currencySymbol: '¥',
submit: '提交',
searchTip: '请输入搜索关键词'
},
//公共
common: {
//语言包名称
name: '中文',
mescrollTextInOffset: '下拉刷新',
mescrollTextOutOffset: '释放更新',
mescrollEmpty: "暂无相关数据",
goodsRecommendTitle: '猜你喜欢',
currencySymbol: '¥',
submit: '提交',
searchTip: '请输入搜索关键词',
// 版本更新
updateTip: '更新提示',
updateReady: '新版本已经准备好,是否重启应用?',
// 网络状态
networkLost: '网络失去链接',
networkCheck: '请检查网络链接',
// 配置相关
config: {
siteJsNotConfigured: '小程序发行环境下,必须配置site.js文件,不然将使用默认配置,影响小程序定制分发!'
},
// 平台检测
platform: {
wechatOfficialAccount: '微信公众号',
wechatMiniProgram: '微信小程序',
alipayMiniProgram: '支付宝小程序',
baiduMiniProgram: '百度小程序',
toutiaoMiniProgram: '头条小程序',
qqMiniProgram: 'QQ小程序',
kuaishouMiniProgram: '快手小程序',
jdMiniProgram: '京东小程序',
miniProgram360: '360小程序',
xhsMiniProgram: '小红书小程序',
harmonyService: '鸿蒙元服务',
app: 'APP',
pc: 'PC端',
h5: 'H5',
unknown: '未知平台',
},
},
// 导航工具
navigation: {
serviceUnavailable: '导航服务暂时不可用',
},
//客服
customer: {
weChatKefu: '微信客服',
@@ -58,6 +91,7 @@ export const lang = {
serviceUnavailable: '客服暂不可用',
wxworkLinkNotConfigured: '企业微信链接未配置',
fromFloatButton: '来自悬浮按钮的咨询',
serviceTypeNotConfigured: '客服类型未配置',
},
// 企业微信JS-SDK
@@ -153,6 +187,8 @@ export const lang = {
weekThursday: '周四',
weekFriday: '周五',
weekSaturday: '周六',
// 错误消息
deepCloneNotObject: 'obj 不是一个对象!',
},
// AI 客服服务
@@ -212,11 +248,6 @@ export const lang = {
logoutTitle: '提示',
logoutContent: '确定要退出登录吗',
// 导航工具
navigation: {
serviceUnavailable: '导航服务暂时不可用',
},
// 微信地图JSSDK
mapWxJssdk: {
// 错误提示

View File

@@ -1,7 +1,16 @@
export const lang = {
expand: '展开',
clickToCollapse: '点击收起',
reachedBottom: '已经到底了~',
noCategory: '暂时没有分类哦!',
pieceUnit: '件',
'components-diy': {
diyCategory: {
// 展开全部分类(横向滚动右侧展开按钮)
expand: '展开',
// 点击收起(展开弹窗底部收起按钮)
clickToCollapse: '点击收起',
// 滚动到底部提示
reachedBottom: '已经到底了~',
// 暂无分类(空状态提示)
noCategory: '暂时没有分类哦!',
// 商品单位:件
pieceUnit: '件',
},
},
}

View File

@@ -0,0 +1,7 @@
export const lang = {
// 销量文案:已售X件
soldCount: '已售{count}{unit}',
// 销量单位:件
soldUnit: '件',
}

View File

@@ -0,0 +1,14 @@
export const lang = {
'components-diy': {
diyIndexPage: {
// 首页(默认分类名称)
home: '首页',
// 销量文案:已售X件
soldCount: '已售{count}{unit}',
// 销量单位:件
soldUnit: '件',
// 分类下无商品的空状态提示
noGoodsInCategory: '该分类下暂无商品',
},
},
}

View File

@@ -0,0 +1,4 @@
export const lang = {
notice: '公告',
gotIt: '我知道了',
}

View File

@@ -1,6 +1,14 @@
export const lang = {
switchStore: '切换',
locating: '定位中...',
gettingLocation: '获取当前位置...',
searchPlaceholder: '商品搜索',
}
'components-diy': {
diyStore: {
// 切换门店(按钮文案)
switchStore: '切换门店',
// 定位中(加载状态提示)
locating: '定位中',
// 获取位置中(地址加载状态提示)
gettingLocation: '获取位置中',
// 搜索框占位符
searchPlaceholder: '搜索',
},
},
};

View File

@@ -0,0 +1,6 @@
export const lang = {
// H5端预加载视频失败
h5PreloadFailed: 'H5端预加载视频失败',
// 视频加载失败
videoLoadFailed: '视频加载失败',
}

View File

@@ -0,0 +1,22 @@
export const lang = {
// mescroll 下拉刷新/上拉加载组件
components: {
mescroll: {
// 下拉刷新提示文本
down: {
textInOffset: '下拉刷新',
textOutOffset: '释放更新',
textLoading: '加载中 ...',
},
// 上拉加载提示文本
up: {
textLoading: '加载中 ...',
textNoMore: '-- END --',
},
// 空布局提示
empty: {
tip: '~ 暂无相关数据 ~',
},
},
},
}

View File

@@ -1,4 +1,18 @@
export const lang = {
//title为每个页面的标题
title: '分销商',
// 会员信息
joinTime: '加入时间:',
// 统计单位
peopleUnit: '人',
orderUnit: '单',
currencyUnit: '元',
// 空状态
noData: '暂无数据',
// Toast 提示
merchantNotOpenFenxiao: '商家未开启分销',
}

View File

@@ -1,4 +1,9 @@
export const lang = {
//title为每个页面的标题
title: '查看文件',
searchPlaceholder: '请输入您要搜索的文件名称',
viewPdf: '查看PDF文件',
emptyText: '暂无文件',
loading: '正在加载',
fileNotExist: '文件不存在',
}

15
lang/zh-cn/form/form.js Normal file
View File

@@ -0,0 +1,15 @@
export const lang = {
// 表单页面
form: {
form: {
// 页面标题
title: '请填写表单所需信息',
// 提交成功提示
submitSuccess: '提交成功',
// 未获取到表单信息
noFormInfo: '未获取到表单信息',
},
},
}

View File

@@ -0,0 +1,48 @@
export const lang = {
// title为每个页面的标题
title: '绑定手机号',
bindMobile: {
// 一键授权绑定
oneClickAuthBind: '一键授权绑定',
// 手动绑定
manualBind: '手动绑定',
// 手机号
phoneNumber: '手机号',
// 请输入手机号
pleInputMobile: '请输入手机号',
// 请输入正确的手机号
surePhoneNumber: '请输入正确的手机号',
// 验证码
confirmCode: '验证码',
// 请输入验证码
confirmCodeInput: '请输入验证码',
// 短信验证码
smsCode: '短信验证码',
// 请输入动态码
animateCodeInput: '请输入动态码',
// 获取动态码
findanimateCode: '获取动态码',
// 已发送
codeSent: '已发送({seconds}s)',
// 请勿重复点击
doNotRepeatedlyClick: '请勿重复点击',
// 保存/绑定
save: '绑定',
// 绑定成功
bindSuccess: '绑定成功',
// 与原手机号一致,无需修改
alikePhone: '与原手机号一致,无需修改',
// 提示文案
tipTitle: '温馨提示',
// 绑定手机号后可享受更多服务
bindBenefitTip: '绑定手机号后可享受更多服务',
// 发送动态码提示
sendCodeTip: '点击"获取动态码",将会向您的手机号{mobile}发送验证码',
},
}

View File

@@ -1,3 +1,10 @@
export const lang = {
//title为每个页面的标题
title: '账号注销',
}
// 协议勾选提示
agreeTermsText: '勾选即表示您已阅读并同意本协议',
// 下一步按钮
nextStep: '下一步',
// 未勾选协议提示
pleaseAgreeFirst: '请先勾选同意协议',
}

View File

@@ -0,0 +1,25 @@
export const lang = {
//title为每个页面的标题
title: '会员卡记录',
// 顶部统计
totalConsume: '累计消费',
cardBalance: '卡余额',
totalRecharge: '累计充值',
yuanSuffix: '元',
statUnit: '({unit})',
// 筛选 tab
filterAll: '全部',
filterIncome: '收入',
filterExpense: '支出',
filterRecharge: '充值',
filterConsume: '消费',
filterGift: '赠送',
// 空状态
emptyTips: '暂无会员卡记录',
// 金额格式
amountWithUnit: '{amount}元',
}

View File

@@ -1,4 +1,7 @@
export const lang = {
//title为每个页面的标题
title: '我的关注'
title: '我的关注',
emptyText: '暂无关注的商品',
emptySubText: '赶紧去逛逛, 关注心仪的商品吧',
deleteSuccess: '删除成功'
}

View File

@@ -1,4 +1,9 @@
export const lang = {
//title为每个页面的标题
title: '修改头像'
title: '修改头像',
clickToUpload: '点击上传',
confirmSave: '确认保存',
reupload: '重新上传',
uploadFailed: '头像上传失败',
modifySuccess: '头像修改成功'
}

View File

@@ -1,4 +1,5 @@
export const lang = {
//title为每个页面的标题
title: '提现记录'
title: '提现记录',
emptyTips: '暂无提现记录'
}

View File

@@ -1,7 +1,38 @@
export const lang = {
//title为每个页面的标题
title: '商品列表',
// 搜索框 placeholder
searchPlaceholder: '请输入您要搜索的商品',
// 按钮默认文本
buyText: '购买',
// 排序
sort: {
comprehensive: '综合',
sales: '销量',
price: '价格',
filter: '筛选',
},
// 商品列表
goods: {
consultNow: '立即咨询',
sold: '已售{num}{unit}',
defaultUnit: '件',
noGoods: '暂无商品',
},
// 筛选弹窗
filter: {
title: '筛选',
allCategories: '全部分类',
all: '全部',
reset: '重置',
confirm: '确定',
},
// 弹窗提示
modal: {
title: '提示',

View File

@@ -15,5 +15,10 @@ export const lang = {
// 提示信息
toast: {
cancelSuccess: '撤销成功'
},
// 撤销退款弹窗
cancelRefund: {
content: '撤销之后本次申请将会关闭,如后续仍有问题可再次发起申请。',
cancelText: '暂不撤销'
}
}

View File

@@ -0,0 +1,91 @@
export const lang = {
//title为每个页面的标题
title: '发票申请',
// ====== 页头区 ======
header: {
title: '发票信息',
desc: '请填写发票相关信息'
},
// ====== 发票类型 ======
invoiceType: {
title: '发票类型',
paper: '纸质发票',
paperDesc: '发票将邮寄到指定地址',
electronic: '电子发票',
electronicDesc: '发票将发送至您的邮箱'
},
// ====== 发票抬头 ======
invoiceTitle: {
title: '发票抬头',
personal: '个人',
company: '企业',
label: '发票抬头',
personalPlaceholder: '请输入个人姓名',
companyPlaceholder: '请输入企业名称',
taxpayerId: '纳税人识别号',
taxpayerIdPlaceholder: '请输入纳税人识别号'
},
// ====== 发票内容 ======
invoiceContent: {
title: '发票内容',
label: '发票内容',
placeholder: '请选择发票内容'
},
// ====== 邮寄信息(纸质发票) ======
mailInfo: {
title: '邮寄信息',
receiver: '收件人',
receiverPlaceholder: '请输入收件人姓名',
mobile: '联系电话',
mobilePlaceholder: '请输入联系电话',
address: '邮寄地址',
regionPlaceholder: '请选择所在地区',
detailAddress: '详细地址',
detailAddressPlaceholder: '请输入详细地址'
},
// ====== 邮箱信息(电子发票) ======
emailInfo: {
title: '邮箱信息',
receiverEmail: '接收邮箱',
emailPlaceholder: '请输入接收发票的邮箱地址'
},
// ====== 费用区 ======
fee: {
taxFee: '发票税费',
deliveryFee: '邮寄费用'
},
// ====== 底部提交区 ======
footer: {
tip: '温馨提示:发票申请提交后将在{days}个工作日内处理',
submit: '提交申请'
},
// ====== 表单校验提示 ======
validation: {
pleaseEnterTitle: '请输入发票抬头',
pleaseEnterTaxpayerId: '请输入纳税人识别号',
pleaseSelectContent: '请选择发票内容',
pleaseEnterReceiver: '请输入收件人姓名',
pleaseEnterMobile: '请输入联系电话',
pleaseSelectRegion: '请选择所在地区',
pleaseEnterAddress: '请输入详细地址',
pleaseEnterEmail: '请输入接收邮箱',
invalidEmail: '请输入正确的邮箱地址'
},
// ====== 接口消息 ======
api: {
getConfigFailed: '获取发票配置失败',
submitSuccess: '发票申请提交成功',
submitFailed: '提交失败,请稍后重试',
networkError: '网络错误,请稍后重试'
}
}

View File

@@ -1,4 +1,16 @@
export const lang = {
//title为每个页面的标题
title: '物流信息'
title: '物流信息',
// 订单物流信息
order: {
logistics: {
// 快递公司信息
expressCompany: '承运公司:',
trackingNo: '运单号:',
// 提示消息
orderInfoNotFound: '未获取到订单信息!'
}
}
}

View File

@@ -64,6 +64,12 @@ export const lang = {
cancelSuccess: '撤销成功'
},
// ====== 撤销退款弹窗 ======
cancelRefund: {
content: '撤销之后本次申请将会关闭,如后续仍有问题可再次发起申请。',
cancelText: '暂不撤销'
},
// ====== 表单验证 ======
validate: {
pleaseInputExpressCompany: '请输入物流公司',

View File

@@ -1,17 +1,27 @@
export const lang = {
// 页面标题
title: '搜索结果',
// 操作按钮
exportImage: '导出图片',
exportPdf: '导出PDF',
// 印章详情
seal: {
medium: {
detail: {
// 页面标题
title: '印章详情',
// 导出图片
exportImage: '导出图片',
// 导出PDF
exportPdf: '导出PDF',
// 加载中
loading: '加载中',
// 文件不存在
fileNotExist: '文件不存在',
// 保存成功
saveSuccess: '保存成功',
// 保存失败
saveFailed: '保存失败',
// 下载失败
downloadFailed: '下载失败',
}
}
}
// 加载提示
loading: '正在加载',
// 提示消息
fileNotExist: '文件不存在',
saveSuccess: '保存成功',
saveFailed: '保存失败,请稍后重试',
downloadFailed: '下载失败',
}

View File

@@ -1,23 +1,23 @@
export const lang = {
//title为每个页面的标题
title: '介质材料搜索',
// 印章搜索页
seal: {
medium: {
search: {
// 搜索框占位符
placeholder: "请输入介质名称 '如汽油'",
placeholder: '请输入关键词搜索',
// 搜索结果标题
searchResult: '搜索结果',
// 空状态文案
// 空数据提示
noData: '暂无数据',
// 弹窗标题
tip: '提示',
// 删除全部历史记录确认
confirmDeleteAllHistory: '确认删除全部历史记录?',
// 删除单条历史记录确认
confirmDeleteHistory: '确认删除该条历史记录?',
// 确认删除全部历史记录
confirmDeleteAllHistory: '确定要删除全部搜索历史吗?',
// 确认删除单条历史记录
confirmDeleteHistory: '确定要删除这条搜索历史吗?',
}
}
}
}

View File

@@ -289,27 +289,27 @@ export default {
getOrderStatus() {
this.statusList = [{
status: 'all',
name: '全部',
name: this.$lang('status.all'),
id: 'status_0'
},
{
status: 'waitpay',
name: '待付款',
name: this.$lang('status.waitpay'),
id: 'status_1'
},
{
status: 'waitsend',
name: '待发货',
name: this.$lang('status.waitsend'),
id: 'status_2'
},
{
status: 'waitconfirm',
name: '待收货',
name: this.$lang('status.waitconfirm'),
id: 'status_3'
},
{
status: 'waitrate',
name: '已完成',
name: this.$lang('status.waitrate'),
id: 'status_4'
},
/*{

View File

@@ -16,20 +16,20 @@
<view v-if="item.level_name" class="title color-base-border color-base-text">{{
item.level_name }}</view>
</view>
<view class="color-tip font-size-goods-tag">加入时间:{{
<view class="color-tip font-size-goods-tag">{{ $lang('joinTime') }}{{
$util.timeStampTurnTime(item.bind_fenxiao_time).substring(0, 10) }}</view>
</view>
<view class="consume-info">
<view><text>{{ item.one_child_fenxiao_num + item.one_child_num }}</text> 人</view>
<view><text>{{ item.order_num }}</text> 单</view>
<view><text>{{ item.order_money | moneyFormat }}</text> 元</view>
<view><text>{{ item.one_child_fenxiao_num + item.one_child_num }}</text>{{ $lang('peopleUnit') }}</view>
<view><text>{{ item.order_num }}</text>{{ $lang('orderUnit') }}</view>
<view><text>{{ item.order_money | moneyFormat }}</text>{{ $lang('currencyUnit') }}</view>
</view>
</view>
</view>
</view>
</view>
<block v-if="teamList.length == 0 && emptyShow">
<ns-empty text="暂无数据" :isIndex="false"></ns-empty>
<ns-empty :text="$lang('noData')" :isIndex="false"></ns-empty>
</block>
</block>
</mescroll-uni>
@@ -54,7 +54,7 @@ export default {
setTimeout(() => {
if (!this.addonIsExist.fenxiao) {
this.$util.showToast({
title: '商家未开启分销',
title: this.$lang('merchantNotOpenFenxiao'),
mask: true,
duration: 2000
});

View File

@@ -0,0 +1,289 @@
<template>
<view class="content" :style="themeColor">
<view class="head-wrap">
<!-- 搜索区域 -->
<view class="search-wrap uni-flex uni-row">
<view class="flex-item input-wrap">
<input class="uni-input" maxlength="50" v-model="keyword" @confirm="search()"
:placeholder="$lang('searchPlaceholder')" />
<text class="iconfont icon-sousuo3" @click.stop="search()"></text>
</view>
<view class="iconfont" :class="{ 'icon-apps': isList, 'icon-list': !isList }"
@click="changeListStyle()"></view>
</view>
<!-- 排序 -->
<view class="sort-wrap">
<view class="comprehensive-wrap" :class="{ 'color-base-text': orderType === '' }"
@click="sortTabClick('')">
<text :class="{ 'color-base-text': orderType === '' }">{{ $lang('sort.comprehensive') }}</text>
</view>
<view :class="{ 'color-base-text': orderType === 'sale_num' }" @click="sortTabClick('sale_num')">{{ $lang('sort.sales') }}
</view>
<view class="price-wrap" @click="sortTabClick('discount_price')">
<text
:class="{ 'color-base-text': orderType === 'discount_price' }">{{ $lang('sort.price') }}</text>
<view class="iconfont-wrap">
<view class="iconfont icon-iconangledown-copy asc"
:class="{ 'color-base-text': priceOrder === 'asc' && orderType === 'discount_price' }">
</view>
<view class="iconfont icon-iconangledown desc"
:class="{ 'color-base-text': priceOrder === 'desc' && orderType === 'discount_price' }">
</view>
</view>
</view>
<view :class="{ 'color-base-text': orderType === 'screen' }" class="screen-wrap" @click="sortTabClick('screen')">
<text @click="sortTabClick('screen')">{{ $lang('sort.filter') }}</text>
<view @click="sortTabClick('screen')" class="iconfont-wrap">
<view class="iconfont icon-shaixuan color-tip"></view>
</view>
</view>
</view>
</view>
<mescroll-uni top="180" ref="mescroll" @getData="getGoodsList">
<block slot="list">
<view v-if="isList" class="goods-list single-column" :class="{ show: isList }">
<view class="goods-item margin-bottom" v-for="(item, index) in goodsList" :key="item.id"
@click="toDetail(item)">
<view class="goods-img">
<image :src="goodsImg(item.goods_image)" mode="widthFix" @error="imgError(index)" show-menu-by-longpress ></image>
<view class="color-base-bg goods-tag" v-if="goodsTag(item) != ''">{{ goodsTag(item) }}
</view>
<view class="sell-out" v-if="item.stock <= 0">
<text class="iconfont icon-shuqing"></text>
</view>
</view>
<view class="info-wrap">
<view class="name-wrap">
<view class="goods-name"
:class="[{ 'using-hidden': config.nameLineMode == 'single' }, { 'multi-hidden': config.nameLineMode == 'multiple' }]">
{{ isEnEnv ? item.en_goods_name : item.goods_name }}
</view>
</view>
<view class="lineheight-clear">
<view class="discount-price" v-if="item.isinformation == 0">
<text class="unit price-style small">{{ $lang('common.currencySymbol') }}</text>
<text class="price price-style large">{{
parseFloat(showPrice(item)).toFixed(2).split('.')[0] }}</text>
<text class="unit price-style small">.{{
parseFloat(showPrice(item)).toFixed(2).split('.')[1] }}</text>
</view>
<view class="discount-price" v-else>
<text class="price price-style large">{{ $lang('goods.consultNow') }}</text>
</view>
</view>
<view class="pro-info" v-if="item.isinformation == 0">
<view class="delete-price color-tip price-font" v-if="showMarketPrice(item)">
<text class="unit">{{ $lang('common.currencySymbol') }}</text>
<text>{{ showMarketPrice(item) }}</text>
</view>
<view class="block-wrap">
<view class="sale color-tip" v-if="item.sale_show">{{ $lang('goods.sold', { num: item.sale_num, unit: item.unit || $lang('goods.defaultUnit') }) }}</view>
</view>
<view class="cart-action-wrap" v-if="config.control && item.is_virtual == 0">
<!-- 购物车图标 -->
<view v-if="config.style == 'icon-cart'" :style="{
color: config.theme == 'diy' ? config.textColor : '',
borderColor: config.theme == 'diy' ? config.textColor : ''
}" class="cart shopping-cart-btn iconfont icon-gouwuche click-wrap" :id="'goods-' + item.id"
@click.stop="$refs.goodsSkuIndex.addCart(config.cartEvent, item, $event)">
<view class="click-event"></view>
</view>
<!--加号图标 -->
<view v-else-if="config.style == 'icon-add'" :style="{
color: config.theme == 'diy' ? config.textColor : '',
borderColor: config.theme == 'diy' ? config.textColor : ''
}" class="cart plus-sign-btn iconfont icon-add1 click-wrap" :id="'goods-' + item.id"
@click.stop="$refs.goodsSkuIndex.addCart(config.cartEvent, item, $event)">
<view class="click-event"></view>
</view>
<!-- 按钮 -->
<view v-else-if="config.style == 'button'" :style="{
backgroundColor: config.theme == 'diy' ? config.bgColor : '',
color: config.theme == 'diy' ? config.textColor : '',
fontWeight: config.theme == 'diy' ? (config.fontWeight ? 'bold' : 'normal') : '',
padding: config.theme == 'diy' ? '12rpx ' + config.padding * 2 + 'rpx' : ''
}" class="cart buy-btn click-wrap" :id="'goods-' + item.id"
@click.stop="$refs.goodsSkuIndex.addCart(config.cartEvent, item, $event)">
{{ config.text }}
<view class="click-event"></view>
</view>
<!--自定义图标 -->
<view v-else-if="config.style == 'icon-diy'" :style="{
color: config.theme == 'diy' ? config.textColor : ''
}" class="icon-diy click-wrap" :id="'goods-' + item.id"
@click.stop="$refs.goodsSkuIndex.addCart(config.cartEvent, item, $event)">
<view class="click-event"></view>
<diy-icon :icon="config.iconDiy.icon"
:value="config.iconDiy.style ? config.iconDiy.style : null"></diy-icon>
</view>
</view>
</view>
</view>
</view>
</view>
<view class="goods-list double-column" :class="{ show: !isList }">
<view class="goods-item margin-bottom" v-for="(item, index) in goodsList" :key="item.id"
@click="toDetail(item)"
:style="{ left: listPosition[index] ? listPosition[index].left : '', top: listPosition[index] ? listPosition[index].top : '' }">
<view class="goods-img">
<image :src="goodsImg(item.goods_image)" mode="widthFix" @error="imgError(index)" show-menu-by-longpress></image>
<view class="color-base-bg goods-tag" v-if="goodsTag(item) != ''">{{ goodsTag(item) }}
</view>
<view class="sell-out" v-if="item.stock <= 0">
<text class="iconfont icon-shuqing"></text>
</view>
</view>
<view class="info-wrap">
<view class="goods-name"
:class="[{ 'using-hidden': config.nameLineMode == 'single' }, { 'multi-hidden': config.nameLineMode == 'multiple' }]">
{{ isEnEnv ? item.en_goods_name : item.goods_name }}
</view>
<view class="lineheight-clear">
<view class="discount-price" v-if="item.isinformation == 0">
<text class="unit price-style small">{{ $lang('common.currencySymbol') }}</text>
<text class="price price-style large">{{
parseFloat(showPrice(item)).toFixed(2).split('.')[0] }}</text>
<text class="unit price-style small">.{{
parseFloat(showPrice(item)).toFixed(2).split('.')[1] }}</text>
</view>
<view class="discount-price" v-else>
<text class="price price-style large">{{ $lang('goods.consultNow') }}</text>
</view>
<view class="member-price-tag"
v-if="item.member_price && item.member_price == showPrice(item)">
<image :src="$util.img('public/uniapp/index/VIP.png')" mode="widthFix" show-menu-by-longpress></image>
</view>
<view class="member-price-tag" v-else-if="item.promotion_type == 1">
<image :src="$util.img('public/uniapp/index/discount.png')" mode="widthFix" show-menu-by-longpress></image>
</view>
<view class="delete-price color-tip price-font" v-if="showMarketPrice(item)">
<text class="unit">{{ $lang('common.currencySymbol') }}</text>
<text>{{ showMarketPrice(item) }}</text>
</view>
</view>
<view class="pro-info" v-if="item.isinformation == 0">
<view class="block-wrap">
<view class="sale color-tip" v-if="item.sale_show">{{ $lang('goods.sold', { num: item.sale_num, unit: item.unit || $lang('goods.defaultUnit') }) }}</view>
</view>
<view class="cart-action-wrap" v-if="config.control && item.is_virtual == 0">
<!-- 购物车图标 -->
<view v-if="config.style == 'icon-cart'" :style="{
color: config.theme == 'diy' ? config.textColor : '',
borderColor: config.theme == 'diy' ? config.textColor : ''
}" class="cart shopping-cart-btn iconfont icon-gouwuche click-wrap" :id="'goods-' + item.id"
@click.stop="$refs.goodsSkuIndex.addCart(config.cartEvent, item, $event)">
<view class="click-event"></view>
</view>
<!--加号图标 -->
<view v-else-if="config.style == 'icon-add'" :style="{
color: config.theme == 'diy' ? config.textColor : '',
borderColor: config.theme == 'diy' ? config.textColor : ''
}" class="cart plus-sign-btn iconfont icon-add1 click-wrap" :id="'goods-' + item.id"
@click.stop="$refs.goodsSkuIndex.addCart(config.cartEvent, item, $event)">
<view class="click-event"></view>
</view>
<!-- 按钮 -->
<view v-else-if="config.style == 'button'" :style="{
backgroundColor: config.theme == 'diy' ? config.bgColor : '',
color: config.theme == 'diy' ? config.textColor : '',
fontWeight: config.theme == 'diy' ? (config.fontWeight ? 'bold' : 'normal') : '',
padding: config.theme == 'diy' ? '12rpx ' + config.padding * 2 + 'rpx' : ''
}" class="cart buy-btn click-wrap" :id="'goods-' + item.id"
@click.stop="$refs.goodsSkuIndex.addCart(config.cartEvent, item, $event)">
{{ config.text }}
<view class="click-event"></view>
</view>
<!--自定义图标 -->
<view v-else-if="config.style == 'icon-diy'" :style="{
color: config.theme == 'diy' ? config.textColor : ''
}" class="icon-diy click-wrap" :id="'goods-' + item.id"
@click.stop="$refs.goodsSkuIndex.addCart(config.cartEvent, item, $event)">
<view class="click-event"></view>
<diy-icon :icon="config.iconDiy.icon"
:value="config.iconDiy.style ? config.iconDiy.style : null"></diy-icon>
</view>
</view>
</view>
</view>
</view>
</view>
<view v-if="goodsList.length == 0 && emptyShow"><ns-empty :text="$lang('goods.noGoods')"></ns-empty></view>
</block>
</mescroll-uni>
<ns-goods-sku-index ref="goodsSkuIndex" @addCart="addCart"></ns-goods-sku-index>
<!-- 筛选弹出框 -->
<uni-drawer :visible="showScreen" mode="right" @close="showScreen = false" class="screen-wrap">
<view class="title color-tip">{{ $lang('filter.title') }}</view>
<scroll-view scroll-y>
<!-- 分类筛选项 -->
<view class="category-list-wrap">
<text class="first">{{ $lang('filter.allCategories') }}</text>
<view class="class-box">
<view @click="selectedCategory('')" class="list-wrap">
<text :class="{ selected: !categoryId, 'color-base-text': !categoryId }">{{ $lang('filter.all') }}</text>
</view>
<view @click="selectedCategory(item.category_id)" v-for="(item, index) in categoryList"
:key="index" class="list-wrap">
<text
:class="{ selected: item.category_id == categoryId, 'color-base-text': item.category_id == categoryId }">{{
item.category_name }}</text>
</view>
</view>
</view>
</scroll-view>
<view class="footer" :class="{ 'safe-area': isIphoneX }">
<button type="default" class="footer-box" @click="resetData">{{ $lang('filter.reset') }}</button>
<button type="primary" class="footer-box1" @click="screenData">{{ $lang('filter.confirm') }}</button>
</view>
</uni-drawer>
<loading-cover ref="loadingCover"></loading-cover>
<!-- 隐私弹窗 -->
<privacy-popup ref="privacyPopup"></privacy-popup>
<!-- 底部tabBar -->
<view id="tab-bar">
<diy-bottom-nav></diy-bottom-nav>
</view>
</view>
</template>
<script>
import list from './public/js/list.js';
export default {
data() {
return {};
},
mixins: [list],
methods: {}
};
</script>
<style lang="scss">
@import './public/css/list.scss';
</style>
<style scoped>
>>>.uni-tag--primary.uni-tag--inverted {
background-color: #f5f5f5 !important;
}
::v-deep .sku-layer .uni-popup__wrapper.uni-custom .uni-popup__wrapper-box {
max-height: unset !important;
}
</style>

View File

@@ -1125,18 +1125,18 @@ _handleStreamError(msg) {
generateAIResponse(userMessage) {
const responses = {
'你好': '您好!我是AI智能客服,很高兴为您服务!有什么可以帮助您的吗?',
'价格': '我们的产品价格根据型号和配置有所不同,具体价格请查看商品详情页面。',
'发货': '一般情况下,订单会在24小时内发货,具体时效请查看物流信息。',
'退货': '支持7天无理由退货,请确保商品完好无损。',
'客服': '我们的客服工作时间是9:00-18:00,有问题可以随时联系我们。'
'你好': this.$lang('autoReplyGreeting'),
'价格': this.$lang('autoReplyPrice'),
'发货': this.$lang('autoReplyShipping'),
'退货': this.$lang('autoReplyReturn'),
'客服': this.$lang('autoReplyService')
};
for (let key in responses) {
if (userMessage.includes(key)) {
return responses[key];
}
}
return `感谢您的咨询!关于"${userMessage}"的问题,我们的专业客服会尽快为您解答。您也可以查看我们的帮助文档获取更多信息。`;
return this.$lang('autoReplyDefault', { question: userMessage });
},

View File

@@ -143,7 +143,7 @@ export default {
},
onShow() {
console.log('[AI Chat Page] onShow 触发');
console.log(this.$lang('logOnShowTriggered'));
// 监听键盘高度变化(H5 可能不支持,try-catch 保护)
try {
this._keyboardChangeHandler = (res) => {
@@ -154,7 +154,7 @@ export default {
};
uni.onKeyboardHeightChange(this._keyboardChangeHandler);
} catch (e) {
console.warn('onKeyboardHeightChange 不可用:', e.message);
console.warn(this.$lang('logKeyboardChangeUnavailable') + ':', e.message);
}
// 每次页面显示时尝试加载历史记录(H5可能组件尚未创建,用 nextTick 确保 ref 就绪)
this.$nextTick(() => {
@@ -177,7 +177,7 @@ export default {
},
onUnload() {
console.log('[AI Chat Page] onUnload 触发');
console.log(this.$lang('logOnUnloadTriggered'));
this.cleanup();
aiService.disconnectWebSocket();
},
@@ -233,7 +233,7 @@ export default {
},
handleEventError(error, event) {
console.error('事件处理错误:', {
console.error(this.$lang('logEventHandleError') + ':', {
error: error.message,
eventType: event?.type,
component: this. $options.name
@@ -263,7 +263,7 @@ export default {
this.calculateScrollViewHeight();
this.setupNavigationEvents();
} catch (error) {
console.error('初始化导航栏失败:', error);
console.error(this.$lang('logInitNavbarFailed') + ':', error);
this.setFallbackNavigationValues();
}
},
@@ -459,7 +459,7 @@ export default {
sendProductToAI(product) {
if (!this.$refs.chat) {
console.error('聊天组件未初始化')
console.error(this.$lang('logChatComponentNotInit'))
return
}
@@ -503,7 +503,7 @@ export default {
// 如果配置未加载,延迟重试(最多3次,防白屏)
if (!servicerConfig) {
console.warn('【AI客服】servicerConfig 未加载,200ms后重试...');
console.warn(this.$lang('logServicerConfigNotLoaded'));
setTimeout(() => {
this.checkAccessPermission();
}, 200);

View File

@@ -2,17 +2,17 @@
<view class="page" :style="themeColor">
<view class="help-title">{{ detail.article_title }}</view>
<view class="help-meta" v-if="detail.is_show_release_time == 1">
<text class="help-time">发表时间: {{ $util.timeStampTurnTime(detail.create_time) }}</text>
<text class="help-time">{{ $lang('publishTime') }} {{ $util.timeStampTurnTime(detail.create_time) }}</text>
</view>
<view class="help-content"><rich-text :nodes="content"></rich-text></view>
<view class="bottom-area">
<view v-if="detail.is_show_read_num == 1">
阅读:
{{ $lang('readCount') }}
<text class="price-font">{{ detail.read_num + detail.initial_read_num }}</text>
</view>
<view v-if="detail.is_show_dianzan_num == 1">
<text class="price-font">{{ detail.dianzan_num + detail.initial_dianzan_num }}</text>
人已赞
{{ $lang('likedSuffix') }}
</view>
</view>
<loading-cover ref="loadingCover"></loading-cover>

View File

@@ -21,7 +21,7 @@
</view>
</view>
</view>
<view v-else class="empty-wrap"><ns-empty text="暂无文章" :isIndex="false"></ns-empty></view>
<view v-else class="empty-wrap"><ns-empty :text="$lang('emptyText')" :isIndex="false"></ns-empty></view>
<loading-cover ref="loadingCover"></loading-cover>
</block>
</mescroll-uni>
@@ -88,7 +88,7 @@ export default {
setPublicShare() {
let shareUrl = this.$config.h5Domain + '/pages_tool/article/list';
this.$util.setPublicShare({
title: '文章列表',
title: this.$lang('title'),
desc: '',
link: shareUrl,
imgUrl: this.siteInfo ? this.$util.img(this.siteInfo.logo_square) : ''
@@ -96,7 +96,7 @@ export default {
}
},
onShareAppMessage(res) {
var title = '文章列表';
var title = this.$lang('title');
var path = '/pages_tool/article/list';
return {
title: title,
@@ -107,7 +107,7 @@ export default {
},
//分享到朋友圈
onShareTimeline() {
var title = '文章列表';
var title = this.$lang('title');
var query = '/pages_tool/article/list';
return {
title: title,

View File

@@ -2,7 +2,7 @@
<view>
<view class="search-wrap uni-flex uni-row">
<view class="flex-item input-wrap">
<input class="uni-input" maxlength="50" placeholder="请输入您要搜索的文件名称" v-model="keyword" @confirm="search"
<input class="uni-input" maxlength="50" :placeholder="$lang('searchPlaceholder')" v-model="keyword" @confirm="search"
@input="__set_model($event, 'keyword')" />
<text class="iconfont icon-sousuo3" @tap="search"></text>
</view>
@@ -20,7 +20,7 @@
<view class="info-wrap">
<view class="title">{{ item.$orig.files_title }}</view>
<view class="read-wrap">
<text>查看PDF文件</text>
<text>{{ $lang('viewPdf') }}</text>
</view>
</view>
</view>
@@ -29,7 +29,7 @@
</block>
<block v-else>
<view class="empty-wrap">
<ns-empty text="暂无文件" :isIndex="false"></ns-empty>
<ns-empty :text="$lang('emptyText')" :isIndex="false"></ns-empty>
</view>
</block>
<loading-cover ref="loadingCover"></loading-cover>
@@ -116,7 +116,7 @@ export default {
},
toDetail(item) {
uni.showLoading({
title: '正在加载'
title: this.$lang('loading')
})
try {
uni.downloadFile({
@@ -141,7 +141,7 @@ export default {
complete: res => {
if (res.statusCode === 404) {
uni.showToast({
title: '文件不存在',
title: this.$lang('fileNotExist'),
icon: 'none'
})
}

View File

@@ -5,12 +5,12 @@
<image :src="$util.img('public/uniapp/form/banner.png')" mode="widthFix"></image>
</view>
<view class="system-form-wrap">
<view class="form-title">请填写表单所需信息</view>
<view class="form-title">{{ $lang('form.form.title') }}</view>
<ns-form :data="detail.json_data" :formConfig="detail.form_config" ref="form"></ns-form>
<button type="primary" size="mini" class="button mini" @click="create()">提交</button>
<button type="primary" size="mini" class="button mini" @click="create()">{{ $lang('common.submit') }}</button>
</view>
</view>
<ns-empty :text="complete ? '提交成功' : '未获取到表单信息'" v-else></ns-empty>
<ns-empty :text="complete ? $lang('form.form.submitSuccess') : $lang('form.form.noFormInfo')" v-else></ns-empty>
<loading-cover ref="loadingCover"></loading-cover>
<ns-login ref="login"></ns-login>
@@ -88,7 +88,7 @@
},
success: res => {
if (res.code == 0) {
this.$util.showToast({ title: '提交成功' })
this.$util.showToast({ title: this.$lang('form.form.submitSuccess') })
setTimeout(() => {
this.complete = true;
this.detail = null;

View File

@@ -0,0 +1,380 @@
<template>
<view :style="themeColor">
<template v-if="memberInfo">
<!-- 一键授权绑定区域(小程序环境) -->
<!-- #ifdef MP -->
<view v-if="!showManualForm" class="bind-wrap">
<view class="bind-tip">
<view class="tip-title">{{ $lang('bindMobile.tipTitle') }}</view>
<view class="tip-desc">{{ $lang('bindMobile.bindBenefitTip') }}</view>
</view>
<view class="save-item bind-mobile">
<button type="primary" open-type="getPhoneNumber" @getphonenumber="mobileAuth">{{ $lang('bindMobile.oneClickAuthBind') }}</button>
</view>
<view class="save-item bind-mobile">
<button type="primary" @click="showManualForm = true">{{ $lang('bindMobile.manualBind') }}</button>
</view>
</view>
<!-- #endif -->
<!-- 手动绑定表单 -->
<view class="edit-info">
<!-- #ifdef MP -->
<view v-if="showManualForm" class="form-switch-back" @click="showManualForm = false">
<text>{{ $lang('bindMobile.oneClickAuthBind') }}</text>
</view>
<!-- #endif -->
<view class="edit-info-box">
<text class="info-name">{{ $lang('bindMobile.phoneNumber') }}</text>
<input class="uni-input info-content" type="number" maxlength="11" :placeholder="$lang('bindMobile.pleInputMobile')" v-model="formData.mobile" />
</view>
<view class="edit-info-box">
<text class="info-name">{{ $lang('bindMobile.confirmCode') }}</text>
<input class="uni-input info-content" type="number" maxlength="4" :placeholder="$lang('bindMobile.confirmCodeInput')" v-model="formData.mobileVercode" />
<image :src="captcha.img" class="captcha" @click="getCaptcha" show-menu-by-longpress></image>
</view>
<view class="edit-info-box">
<text class="info-name">{{ $lang('bindMobile.smsCode') }}</text>
<input class="uni-input info-content" type="number" maxlength="6" :placeholder="$lang('bindMobile.animateCodeInput')" v-model="formData.mobileDynacode" />
<button type="primary" class="dynacode" @click="bindMoblieCode()">{{ formData.mobileCodeText }}</button>
</view>
<view class="save-item" @click="bindMobile">
<button type="primary">{{ $lang('bindMobile.save') }}</button>
</view>
</view>
</template>
<ns-login ref="login"></ns-login>
</view>
</template>
<script>
import validate from '@/common/js/validate.js';
import auth from '@/common/js/auth.js';
export default {
data() {
return {
showManualForm: false,
formData: {
mobile: '',
mobileVercode: '',
mobileDynacode: '',
mobileCodeText: '',
},
seconds: 120,
timer: null,
isSend: false,
captcha: {
id: '',
img: ''
},
};
},
mixins: [auth],
onLoad() {
this.formData.mobileCodeText = this.$lang('bindMobile.findanimateCode');
this.getCaptcha();
// #ifdef MP-WEIXIN
this.getCode();
// #endif
},
onShow() {
if (!this.storeToken) {
this.$nextTick(() => {
this.$refs.login.open('/pages_tool/member/index');
});
}
},
onHide() {
this.seconds = 120;
this.formData.mobileCodeText = this.$lang('bindMobile.findanimateCode');
this.isSend = false;
clearInterval(this.timer);
},
watch: {
seconds(value) {
if (value == 0) {
this.seconds = 120;
this.formData.mobileCodeText = this.$lang('bindMobile.findanimateCode');
this.isSend = false;
clearInterval(this.timer);
}
}
},
computed: {
themeColor() {
return this.$store.state.themeStyle ? `--main_color:${this.$store.state.themeStyle.main_color}` : '';
},
themeStyle() {
return this.$store.state.themeStyle || {};
},
memberInfo() {
return this.$store.state.memberInfo || {};
},
storeToken() {
return this.$store.state.token;
},
},
methods: {
// 获取图形验证码
getCaptcha() {
this.$api.sendRequest({
url: '/api/captcha/captcha',
data: {
captcha_id: this.captcha.id
},
success: res => {
if (res.code >= 0) {
this.captcha = res.data;
this.captcha.img = this.captcha.img.replace(/\r\n/g, '');
}
}
});
},
// 验证手机号
vertifyMobile() {
var rule = [{
name: 'mobile',
checkType: 'required',
errorMsg: this.$lang('bindMobile.pleInputMobile')
}, {
name: 'mobile',
checkType: 'phoneno',
errorMsg: this.$lang('bindMobile.surePhoneNumber')
}];
var checkRes = validate.check(this.formData, rule);
if (!checkRes) {
this.$util.showToast({
title: validate.error
});
return false;
}
return true;
},
// 发送短信动态码
async bindMoblieCode() {
if (this.seconds != 120) return;
var rule = [{
name: 'mobile',
checkType: 'phoneno',
errorMsg: this.$lang('bindMobile.surePhoneNumber')
},
{
name: 'mobileVercode',
checkType: 'required',
errorMsg: this.$lang('bindMobile.confirmCodeInput')
},
];
var checkRes = validate.check(this.formData, rule);
if (checkRes && !this.isSend) {
this.isSend = true;
this.$api.sendRequest({
url: '/api/member/bindmobliecode',
data: {
mobile: this.formData.mobile,
captcha_id: this.captcha.id,
captcha_code: this.formData.mobileVercode
},
success: res => {
let data = res.data;
if (data.key) {
if (this.seconds == 120 && this.timer == null) {
this.timer = setInterval(() => {
this.seconds--;
this.formData.mobileCodeText = this.$lang('bindMobile.codeSent', { seconds: this.seconds });
}, 1000);
}
uni.setStorageSync('mobile_key', data.key);
} else {
this.$util.showToast({
title: res.message
});
this.isSend = false;
}
},
fail: res => {
this.isSend = false;
this.getCaptcha();
}
});
} else {
this.$util.showToast({
title: validate.error ? validate.error : this.$lang('bindMobile.doNotRepeatedlyClick')
});
}
},
// 绑定手机号
bindMobile() {
if (!this.vertifyMobile()) return;
var mobileRule = [{
name: 'mobileVercode',
checkType: 'required',
errorMsg: this.$lang('bindMobile.confirmCodeInput')
},
{
name: 'mobileDynacode',
checkType: 'required',
errorMsg: this.$lang('bindMobile.animateCodeInput')
},
];
var checkRes = validate.check(this.formData, mobileRule);
if (checkRes) {
if (this.memberInfo.mobile && this.formData.mobile == this.memberInfo.mobile) {
this.$util.showToast({
title: this.$lang('bindMobile.alikePhone')
});
return;
}
this.$api.sendRequest({
url: '/api/member/modifymobile',
data: {
mobile: this.formData.mobile,
captcha_id: this.captcha.id,
captcha_code: this.formData.mobileVercode,
code: this.formData.mobileDynacode,
key: uni.getStorageSync("mobile_key"),
},
success: res => {
if (res.code == 0) {
this.memberInfo.mobile = this.formData.mobile;
this.$store.commit('setMemberInfo', this.memberInfo);
this.$util.showToast({
title: this.$lang('bindMobile.bindSuccess')
});
uni.navigateBack({
delta: 1
});
} else {
this.$util.showToast({
title: res.message
});
this.getCaptcha();
}
},
fail: res => {
this.isSend = false;
this.getCaptcha();
}
});
} else {
this.$util.showToast({
title: validate.error
});
}
},
// 一键授权绑定(小程序)
mobileAuth(e) {
if (e.detail.errMsg == 'getPhoneNumber:ok') {
let data = e.detail
// #ifdef MP-WEIXIN
Object.assign(data, this.authInfo)
// #endif
this.$api.sendRequest({
url: '/api/member/mobileauth',
data,
success: res => {
if (res.code == 0) {
this.$util.showToast({
title: this.$lang('bindMobile.bindSuccess')
});
uni.navigateBack({
delta: 1
});
} else {
this.$util.showToast({
title: res.message
});
}
}
})
}
}
}
};
</script>
<style lang="scss">
.bind-wrap {
padding: 60rpx 40rpx;
}
.bind-tip {
text-align: center;
margin-bottom: 60rpx;
.tip-title {
font-size: 36rpx;
font-weight: bold;
color: #333;
margin-bottom: 20rpx;
}
.tip-desc {
font-size: 28rpx;
color: #999;
}
}
.edit-info-box {
margin-top: 20rpx;
display: flex;
align-items: center;
justify-content: space-between;
padding: 20rpx 40rpx;
min-height: 50rpx;
background-color: #fff;
.info-name {
width: 150rpx;
font-size: 28rpx;
text-align: left;
}
.info-content {
flex: 1;
width: 0;
font-size: 28rpx;
padding: 0;
}
.dynacode {
margin: 0;
padding: 0 10rpx;
width: 250rpx;
height: 60rpx;
font-size: 24rpx;
line-height: 60rpx;
word-break: break-all;
}
}
.captcha {
width: 170rpx;
height: 50rpx;
}
.save-item {
margin-top: 50rpx;
button {
font-size: 30rpx;
}
}
.bind-mobile {
button {
border-radius: 60rpx;
}
}
.form-switch-back {
text-align: center;
padding: 20rpx;
font-size: 26rpx;
color: var(--main_color);
}
</style>

View File

@@ -10,9 +10,9 @@
<view class="align-center agreement-btn-select">
<text v-if="isSelect" class="iconfont icon-dui color-base-text" @click="changeSelect"></text>
<text v-else class="iconfont icon-yuan_checkbox" @click="changeSelect"></text>
<text class="agreement-text" @click="changeSelect">勾选即表示您已阅读并同意本协议</text>
<text class="agreement-text" @click="changeSelect">{{ $lang('agreeTermsText') }}</text>
</view>
<button class="btn color-base-bg" @click="next">下一步</button>
<button class="btn color-base-bg" @click="next">{{ $lang('nextStep') }}</button>
</view>
</view>
</view>
@@ -60,7 +60,7 @@ export default {
this.$util.redirectTo('/pages_tool/member/assets');
} else {
this.$util.showToast({
title: '请先勾选同意协议'
title: this.$lang('pleaseAgreeFirst')
});
}
}

View File

@@ -0,0 +1,314 @@
<template>
<view :style="themeColor">
<!-- 顶部统计 -->
<view class="stat-header">
<view class="stat-item">
<view class="stat-value">{{ cardStat.total_consume }}</view>
<view class="stat-label">{{ $lang('totalConsume') }}{{ $lang('statUnit', { unit: $lang('yuanSuffix') }) }}</view>
</view>
<view class="stat-divider"></view>
<view class="stat-item">
<view class="stat-value">{{ cardStat.balance }}</view>
<view class="stat-label">{{ $lang('cardBalance') }}{{ $lang('statUnit', { unit: $lang('yuanSuffix') }) }}</view>
</view>
<view class="stat-divider"></view>
<view class="stat-item">
<view class="stat-value">{{ cardStat.total_recharge }}</view>
<view class="stat-label">{{ $lang('totalRecharge') }}{{ $lang('statUnit', { unit: $lang('yuanSuffix') }) }}</view>
</view>
</view>
<!-- 筛选 tab -->
<scroll-view class="filter-tab" :scroll-x="true" :show-scrollbar="false">
<view v-for="(item, index) in filterList" :key="index"
class="tab-item" :class="{ active: currentFilter === item.value }"
@click="onFilterChange(item.value)">
<text>{{ item.label }}</text>
</view>
</scroll-view>
<!-- 列表 -->
<mescroll-uni @getData="getData" ref="mescroll">
<block slot="list">
<block v-if="logList.length > 0">
<view class="log-list">
<view class="log-item" v-for="(item, index) in logList" :key="index" @click="goDetail(item)">
<view class="log-icon" :class="item.type">
<text class="iconfont" :class="getIconClass(item.type)"></text>
</view>
<view class="log-info">
<view class="log-title">{{ item.title }}</view>
<view class="log-desc">{{ item.desc }}</view>
<view class="log-time">{{ formatTime(item.create_time) }}</view>
</view>
<view class="log-amount">
<text :class="item.amount > 0 ? 'income' : 'expense'">
{{ item.amount > 0 ? '+' : '' }}{{ $lang('amountWithUnit', { amount: item.amount }) }}
</text>
</view>
</view>
</view>
</block>
<block v-else>
<ns-empty :isIndex="false" :text="$lang('emptyTips')"></ns-empty>
</block>
</block>
</mescroll-uni>
<ns-login ref="login"></ns-login>
<loading-cover ref="loadingCover"></loading-cover>
</view>
</template>
<script>
export default {
data() {
return {
cardStat: {
total_consume: '0.00',
balance: '0.00',
total_recharge: '0.00'
},
logList: [],
filterList: [
{ label: this.$lang('filterAll'), value: 'all' },
{ label: this.$lang('filterIncome'), value: 'income' },
{ label: this.$lang('filterExpense'), value: 'expense' },
{ label: this.$lang('filterRecharge'), value: 'recharge' },
{ label: this.$lang('filterConsume'), value: 'consume' },
{ label: this.$lang('filterGift'), value: 'gift' }
],
currentFilter: 'all'
};
},
onLoad() {
if (!this.storeToken) {
this.$nextTick(() => {
this.$refs.login.open('/pages_tool/member/card_log');
});
return;
}
this.loadStat();
},
methods: {
// 加载统计数据
loadStat() {
this.$api.sendRequest({
url: '/api/membercard/stat',
success: res => {
if (res.code == 0 && res.data) {
this.cardStat = res.data;
}
}
});
},
// 筛选切换
onFilterChange(value) {
this.currentFilter = value;
this.$refs.mescroll.refresh();
},
// 获取图标 class
getIconClass(type) {
const iconMap = {
recharge: 'icon-chongzhi',
consume: 'icon-xiaofei',
gift: 'icon-liwu',
refund: 'icon-tuikuan'
};
return iconMap[type] || 'icon-jifen';
},
// 格式化时间
formatTime(timestamp) {
return this.$util.timeStampTurnTime(timestamp);
},
// 跳转详情
goDetail(item) {
if (item.related_type === 'order') {
this.$util.redirectTo('/pages_order/detail', { order_id: item.related_id });
}
},
// 获取列表数据
getData(mescroll) {
this.$api.sendRequest({
url: '/api/membercard/logPage',
data: {
page_size: mescroll.size,
page: mescroll.num,
type: this.currentFilter
},
success: res => {
let newArr = [];
if (res.code == 0 && res.data) {
newArr = res.data.list;
} else {
this.$util.showToast({
title: res.message
});
}
mescroll.endSuccess(newArr.length);
if (mescroll.num == 1) {
this.logList = [];
}
this.logList = this.logList.concat(newArr);
if (this.$refs.loadingCover) this.$refs.loadingCover.hide();
},
fail: res => {
mescroll.endErr();
if (this.$refs.loadingCover) this.$refs.loadingCover.hide();
}
});
}
}
};
</script>
<style lang="scss" scoped>
.stat-header {
display: flex;
align-items: center;
padding: 30rpx;
background: linear-gradient(135deg, #e3b66b 0%, #d4a04a 100%);
color: #fff;
.stat-item {
flex: 1;
text-align: center;
.stat-value {
font-size: 36rpx;
font-weight: bold;
margin-bottom: 8rpx;
}
.stat-label {
font-size: 24rpx;
opacity: 0.85;
}
}
.stat-divider {
width: 1rpx;
height: 60rpx;
background: rgba(255, 255, 255, 0.3);
}
}
.filter-tab {
width: 100%;
white-space: nowrap;
background: #fff;
border-bottom: 1rpx solid $color-line;
.tab-item {
display: inline-block;
padding: 24rpx 32rpx;
font-size: $font-size-base;
color: $color-tip;
position: relative;
&.active {
color: $color-base-text;
font-weight: 500;
&::after {
content: '';
position: absolute;
bottom: 0;
left: 50%;
transform: translateX(-50%);
width: 40rpx;
height: 4rpx;
background: $color-base-text;
border-radius: 2rpx;
}
}
}
}
.log-list {
padding: 0 32rpx;
background: #fff;
.log-item {
display: flex;
align-items: center;
padding: 28rpx 0;
border-bottom: 1rpx solid $color-line;
&:last-child {
border-bottom: none;
}
.log-icon {
width: 72rpx;
height: 72rpx;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin-right: 24rpx;
font-size: 32rpx;
&.recharge {
background: rgba(76, 175, 80, 0.1);
color: #4caf50;
}
&.consume {
background: rgba(244, 67, 54, 0.1);
color: #f44336;
}
&.gift {
background: rgba(255, 152, 0, 0.1);
color: #ff9800;
}
&.refund {
background: rgba(33, 150, 243, 0.1);
color: #2196f3;
}
}
.log-info {
flex: 1;
overflow: hidden;
.log-title {
font-size: $font-size-base;
color: $color-base-text;
font-weight: 500;
margin-bottom: 8rpx;
}
.log-desc {
font-size: $font-size-activity-tag;
color: $color-tip;
margin-bottom: 8rpx;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.log-time {
font-size: $font-size-activity-tag;
color: $color-tip;
}
}
.log-amount {
margin-left: 20rpx;
font-size: $font-size-toolbar;
font-weight: 500;
.income {
color: #4caf50;
}
.expense {
color: $color-base-text;
}
}
}
}
</style>

View File

@@ -13,7 +13,7 @@
<view class="goods_name font-size-base">{{ item.sku_name }}</view>
<view class="goods_opection">
<view class="left lineheight-clear ">
<text class="symbol price-style small">¥</text>
<text class="symbol price-style small">{{ $lang('common.currencySymbol') }}</text>
<text class="price price-style large">{{
parseFloat(item.discount_price).toFixed(2).split('.')[0] }}</text>
<text class="symbol price-style small">.{{
@@ -30,7 +30,7 @@
</block>
<!-- 第一个列表为空时 -->
<view class="collection-empty" v-else>
<ns-empty v-if="collectionList.length == 0 && isShowEmpty" text="暂无关注的商品" subText="赶紧去逛逛, 关注心仪的商品吧"
<ns-empty v-if="collectionList.length == 0 && isShowEmpty" :text="$lang('member.collection.emptyText')" :subText="$lang('member.collection.emptySubText')"
:isIndex="Boolean(storeToken)" :emptyBtn="{ text: $lang('toGoodsCategoryPage'), url: '/pages_goods/category' }"></ns-empty>
<button type="primary" size="mini" class="button mini" v-if="!storeToken"
@click="toLogin">{{ $lang('toLogin') }}</button>

View File

@@ -11,16 +11,16 @@
<view class="opection-box">
<block v-if="newImg == ''">
<!-- #ifdef MP-ALIPAY -->
<button type="primary" @click="uploadFace()">点击上传</button>
<button type="primary" @click="uploadFace()">{{$lang('member.modifyFace.clickToUpload')}}</button>
<!-- #endif -->
<!-- #ifndef MP-ALIPAY -->
<button type="primary" @click="chooseImage()">点击上传</button>
<button type="primary" @click="chooseImage()">{{$lang('member.modifyFace.clickToUpload')}}</button>
<!-- #endif -->
</block>
<block v-else>
<view class="opec">
<button size="mini" class="mini" type="primary" @click="save()">确认保存</button>
<button size="mini" class="mini" type="primary" @click="chooseImage()">重新上传</button>
<button size="mini" class="mini" type="primary" @click="save()">{{$lang('member.modifyFace.confirmSave')}}</button>
<button size="mini" class="mini" type="primary" @click="chooseImage()">{{$lang('member.modifyFace.reupload')}}</button>
</view>
</block>
</view>
@@ -92,7 +92,7 @@ export default {
},
fail: () => {
this.$util.showToast({
title: '头像上传失败'
title: this.$lang('member.modifyFace.uploadFailed')
});
}
});
@@ -114,7 +114,7 @@ export default {
this.memberInfo.headimg = this.imgurl;
this.$store.commit('setMemberInfo', this.memberInfo);
this.$util.showToast({
title: '头像修改成功'
title: this.$lang('member.modifyFace.modifySuccess')
});
setTimeout(() => {
this.$util.redirectTo('/pages_tool/member/info', {}, 'redirectTo');

View File

@@ -53,7 +53,7 @@ export default {
success: res => {
if (res.code == 0) {
this.$util.showToast({
title: "删除成功"
title: this.$lang('member.collection.deleteSuccess')
})
let array = this.collectionList;
let newArray = array.filter((v) => {

View File

@@ -21,7 +21,7 @@
</view>
</block>
<block v-else>
<ns-empty :isIndex="false" text="暂无提现记录"></ns-empty>
<ns-empty :isIndex="false" :text="$lang('emptyTips')"></ns-empty>
</block>
</view>
</mescroll-uni>

View File

@@ -18,12 +18,12 @@
<view class="goods-num">
<view class="num-text color-base-text">{{ item.refund_status_name }}</view>
<view class="num-price">
<text>¥{{ item.price }}</text>
<text>{{ $lang('common.currencySymbol') }}{{ item.price }}</text>
<text class="num">×{{ item.num }}</text>
</view>
</view>
</view>
</view>
</view>
<view class="goods-btn">
<view class="btn-text">
<text>{{ $lang('totalItems', { num: item.num }) }}</text>

View File

@@ -0,0 +1,518 @@
<template>
<view :style="themeColor" class="invoice-container">
<view class="invoice-header">
<view class="header-title">{{ $lang('header.title') }}</view>
<view class="header-desc">{{ $lang('header.desc') }}</view>
</view>
<view class="invoice-section">
<view class="section-title">{{ $lang('invoiceType.title') }}</view>
<view class="invoice-type-list">
<view
class="type-item"
:class="{ active: formData.invoice_type == 1 }"
@click="selectType(1)"
>
<view class="type-icon">
<text class="iconfont icon-zhifubao"></text>
</view>
<view class="type-name">{{ $lang('invoiceType.paper') }}</view>
<view class="type-desc">{{ $lang('invoiceType.paperDesc') }}</view>
</view>
<view
class="type-item"
:class="{ active: formData.invoice_type == 2 }"
@click="selectType(2)"
>
<view class="type-icon">
<text class="iconfont icon-weixinzhifu"></text>
</view>
<view class="type-name">{{ $lang('invoiceType.electronic') }}</view>
<view class="type-desc">{{ $lang('invoiceType.electronicDesc') }}</view>
</view>
</view>
</view>
<view class="invoice-section">
<view class="section-title">{{ $lang('invoiceTitle.title') }}</view>
<view class="title-type-switch">
<view
class="switch-item"
:class="{ active: formData.invoice_title_type == 1 }"
@click="selectTitleType(1)"
>
{{ $lang('invoiceTitle.personal') }}
</view>
<view
class="switch-item"
:class="{ active: formData.invoice_title_type == 2 }"
@click="selectTitleType(2)"
>
{{ $lang('invoiceTitle.company') }}
</view>
</view>
<view class="form-item">
<view class="form-label">
<text class="required">*</text>
{{ $lang('invoiceTitle.label') }}
</view>
<input
class="form-input"
type="text"
v-model="formData.invoice_title"
:placeholder="formData.invoice_title_type == 1 ? $lang('invoiceTitle.personalPlaceholder') : $lang('invoiceTitle.companyPlaceholder')"
/>
</view>
<view class="form-item" v-if="formData.invoice_title_type == 2">
<view class="form-label">
<text class="required">*</text>
{{ $lang('invoiceTitle.taxpayerId') }}
</view>
<input
class="form-input"
type="text"
v-model="formData.taxpayer_id"
:placeholder="$lang('invoiceTitle.taxpayerIdPlaceholder')"
/>
</view>
</view>
<view class="invoice-section">
<view class="section-title">{{ $lang('invoiceContent.title') }}</view>
<view class="form-item">
<view class="form-label">{{ $lang('invoiceContent.label') }}</view>
<picker mode="selector" :range="invoiceContentList" range-key="name" @change="onContentChange">
<view class="picker-value">
<text v-if="formData.invoice_content">{{ formData.invoice_content }}</text>
<text class="placeholder" v-else>{{ $lang('invoiceContent.placeholder') }}</text>
<text class="iconfont icon-youjiantou"></text>
</view>
</picker>
</view>
</view>
<view class="invoice-section" v-if="formData.invoice_type == 1">
<view class="section-title">{{ $lang('mailInfo.title') }}</view>
<view class="form-item">
<view class="form-label">
<text class="required">*</text>
{{ $lang('mailInfo.receiver') }}
</view>
<input
class="form-input"
type="text"
v-model="formData.receiver_name"
:placeholder="$lang('mailInfo.receiverPlaceholder')"
/>
</view>
<view class="form-item">
<view class="form-label">
<text class="required">*</text>
{{ $lang('mailInfo.mobile') }}
</view>
<input
class="form-input"
type="number"
v-model="formData.receiver_mobile"
:placeholder="$lang('mailInfo.mobilePlaceholder')"
/>
</view>
<view class="form-item">
<view class="form-label">
<text class="required">*</text>
{{ $lang('mailInfo.address') }}
</view>
<pick-regions :level="3" @finish="onRegionFinish">
<view class="picker-value">
<text v-if="formData.region">{{ formData.region }}</text>
<text class="placeholder" v-else>{{ $lang('mailInfo.regionPlaceholder') }}</text>
<text class="iconfont icon-youjiantou"></text>
</view>
</pick-regions>
</view>
<view class="form-item">
<view class="form-label">
<text class="required">*</text>
{{ $lang('mailInfo.detailAddress') }}
</view>
<input
class="form-input"
type="text"
v-model="formData.address"
:placeholder="$lang('mailInfo.detailAddressPlaceholder')"
/>
</view>
</view>
<view class="invoice-section" v-if="formData.invoice_type == 2">
<view class="section-title">{{ $lang('emailInfo.title') }}</view>
<view class="form-item">
<view class="form-label">
<text class="required">*</text>
{{ $lang('emailInfo.receiverEmail') }}
</view>
<input
class="form-input"
type="text"
v-model="formData.invoice_email"
:placeholder="$lang('emailInfo.emailPlaceholder')"
/>
</view>
</view>
<view class="invoice-fee" v-if="invoiceData && invoiceData.invoice_money > 0">
<view class="fee-item">
<text class="fee-label">{{ $lang('fee.taxFee') }}</text>
<text class="fee-value">¥{{ invoiceData.invoice_money }}</text>
</view>
<view class="fee-item" v-if="formData.invoice_type == 1 && invoiceData.invoice_delivery_money > 0">
<text class="fee-label">{{ $lang('fee.deliveryFee') }}</text>
<text class="fee-value">¥{{ invoiceData.invoice_delivery_money }}</text>
</view>
</view>
<view class="invoice-footer">
<view class="footer-tip">{{ $lang('footer.tip', { days: 3 }) }}</view>
<view class="submit-btn" @click="submitInvoice">{{ $lang('footer.submit') }}</view>
</view>
<loading-cover ref="loadingCover"></loading-cover>
</view>
</template>
<script>
export default {
data() {
return {
orderId: 0,
formData: {
invoice_type: 2,
invoice_title_type: 1,
invoice_title: '',
taxpayer_id: '',
invoice_content: '',
receiver_name: '',
receiver_mobile: '',
region: '',
region_id: '',
address: '',
invoice_email: ''
},
invoiceContentList: [],
invoiceData: null
};
},
onLoad(options) {
if (options.order_id) {
this.orderId = options.order_id;
}
this.getInvoiceConfig();
},
methods: {
selectType(type) {
this.formData.invoice_type = type;
},
selectTitleType(type) {
this.formData.invoice_title_type = type;
this.formData.invoice_title = '';
this.formData.taxpayer_id = '';
},
onContentChange(e) {
const index = e.detail.value;
if (this.invoiceContentList[index]) {
this.formData.invoice_content = this.invoiceContentList[index].name;
}
},
onRegionFinish(region) {
this.formData.region = region.name;
this.formData.region_id = region.id;
},
getInvoiceConfig() {
this.$api.sendRequest({
url: '/api/orderinvoice/config',
data: {
order_id: this.orderId
},
success: res => {
if (res.code == 0 && res.data) {
this.invoiceData = res.data;
this.invoiceContentList = res.data.content_list || [];
if (res.data.invoice_info) {
Object.assign(this.formData, res.data.invoice_info);
}
} else {
this.$util.showToast({
title: res.message || this.$lang('api.getConfigFailed')
});
}
}
});
},
validateForm() {
if (!this.formData.invoice_title) {
this.$util.showToast({
title: this.$lang('validation.pleaseEnterTitle')
});
return false;
}
if (this.formData.invoice_title_type == 2 && !this.formData.taxpayer_id) {
this.$util.showToast({
title: this.$lang('validation.pleaseEnterTaxpayerId')
});
return false;
}
if (!this.formData.invoice_content) {
this.$util.showToast({
title: this.$lang('validation.pleaseSelectContent')
});
return false;
}
if (this.formData.invoice_type == 1) {
if (!this.formData.receiver_name) {
this.$util.showToast({
title: this.$lang('validation.pleaseEnterReceiver')
});
return false;
}
if (!this.formData.receiver_mobile) {
this.$util.showToast({
title: this.$lang('validation.pleaseEnterMobile')
});
return false;
}
if (!this.formData.region_id) {
this.$util.showToast({
title: this.$lang('validation.pleaseSelectRegion')
});
return false;
}
if (!this.formData.address) {
this.$util.showToast({
title: this.$lang('validation.pleaseEnterAddress')
});
return false;
}
}
if (this.formData.invoice_type == 2) {
if (!this.formData.invoice_email) {
this.$util.showToast({
title: this.$lang('validation.pleaseEnterEmail')
});
return false;
}
const emailReg = /^[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+$/;
if (!emailReg.test(this.formData.invoice_email)) {
this.$util.showToast({
title: this.$lang('validation.invalidEmail')
});
return false;
}
}
return true;
},
submitInvoice() {
if (!this.validateForm()) return;
this.$refs.loadingCover.show();
this.$api.sendRequest({
url: '/api/orderinvoice/submit',
data: {
order_id: this.orderId,
...this.formData
},
success: res => {
this.$refs.loadingCover.hide();
if (res.code >= 0) {
this.$util.showToast({
title: this.$lang('api.submitSuccess')
});
setTimeout(() => {
uni.navigateBack();
}, 1500);
} else {
this.$util.showToast({
title: res.message || this.$lang('api.submitFailed')
});
}
},
fail: () => {
this.$refs.loadingCover.hide();
this.$util.showToast({
title: this.$lang('api.networkError')
});
}
});
}
}
};
</script>
<style lang="scss" scoped>
.invoice-container {
min-height: 100vh;
background: #f5f5f5;
padding-bottom: 200rpx;
}
.invoice-header {
background: #fff;
padding: 40rpx 30rpx;
margin-bottom: 20rpx;
.header-title {
font-size: 36rpx;
font-weight: bold;
color: #333;
margin-bottom: 10rpx;
}
.header-desc {
font-size: 26rpx;
color: #999;
}
}
.invoice-section {
background: #fff;
margin-bottom: 20rpx;
padding: 30rpx;
.section-title {
font-size: 30rpx;
font-weight: bold;
color: #333;
margin-bottom: 30rpx;
}
}
.invoice-type-list {
display: flex;
gap: 20rpx;
.type-item {
flex: 1;
border: 2rpx solid #e5e5e5;
border-radius: 12rpx;
padding: 30rpx 20rpx;
text-align: center;
transition: all 0.3s;
&.active {
border-color: #ff6a3d;
background: #fff5f0;
}
.type-icon {
font-size: 48rpx;
margin-bottom: 10rpx;
color: #ff6a3d;
}
.type-name {
font-size: 28rpx;
color: #333;
font-weight: bold;
margin-bottom: 8rpx;
}
.type-desc {
font-size: 22rpx;
color: #999;
}
}
}
.title-type-switch {
display: flex;
border: 2rpx solid #e5e5e5;
border-radius: 8rpx;
margin-bottom: 30rpx;
overflow: hidden;
.switch-item {
flex: 1;
text-align: center;
padding: 20rpx 0;
font-size: 28rpx;
color: #666;
&.active {
background: #ff6a3d;
color: #fff;
}
}
}
.form-item {
display: flex;
align-items: center;
padding: 24rpx 0;
border-bottom: 1rpx solid #f0f0f0;
&:last-child {
border-bottom: none;
}
.form-label {
width: 200rpx;
font-size: 28rpx;
color: #333;
flex-shrink: 0;
.required {
color: #ff4d4f;
margin-right: 4rpx;
}
}
.form-input {
flex: 1;
font-size: 28rpx;
color: #333;
text-align: right;
}
.picker-value {
flex: 1;
display: flex;
align-items: center;
justify-content: flex-end;
font-size: 28rpx;
color: #333;
.placeholder {
color: #ccc;
}
.icon-youjiantou {
font-size: 24rpx;
color: #ccc;
margin-left: 10rpx;
}
}
}
.invoice-fee {
background: #fff;
padding: 30rpx;
margin-bottom: 20rpx;
.fee-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10rpx 0;
.fee-label {
font-size: 28rpx;
color: #666;
}
.fee-value {
font-size: 28rpx;
color: #ff4d4f;
font-weight: bold;
}
}
}
.invoice-footer {
position: fixed;
bottom: 0;
left: 0;
right: 0;
background: #fff;
padding: 20rpx 30rpx;
padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
box-shadow: 0 -2rpx 10rpx rgba(0, 0, 0, 0.05);
.footer-tip {
font-size: 22rpx;
color: #999;
text-align: center;
margin-bottom: 20rpx;
}
.submit-btn {
background: linear-gradient(90deg, #ff8a3d, #ff6a3d);
color: #fff;
font-size: 32rpx;
text-align: center;
padding: 24rpx 0;
border-radius: 40rpx;
font-weight: bold;
}
}
</style>

View File

@@ -34,11 +34,11 @@
<view class="company-logo"><image :src="$util.img(packageItem.express_company_image)" show-menu-by-longpress></image></view>
<view class="info">
<view class="company">
<text>承运公司: {{ packageItem.express_company_name }}</text>
<text>{{ $lang('order.logistics.expressCompany') }}{{ packageItem.express_company_name }}</text>
</view>
<view class="no">
<text>
运单号:
{{ $lang('order.logistics.trackingNo') }}
<text class="color-tip">{{ packageItem.delivery_no }}</text>
</text>
<text class="iconfont icon-fuzhi" @click="copyDeliveryNo(packageItem.delivery_no)"></text>
@@ -124,7 +124,7 @@ export default {
if (this.$refs.loadingCover) this.$refs.loadingCover.hide();
} else {
this.$util.showToast({
title: '未获取到订单信息!'
title: this.$lang('order.logistics.orderInfoNotFound')
});
setTimeout(() => {
this.$util.redirectTo('/pages_order/list');

View File

@@ -7,8 +7,8 @@ export default {
*/
cancleRefund(order_goods_id, callback) {
uni.showModal({
content: '撤销之后本次申请将会关闭,如后续仍有问题可再次发起申请。',
cancelText: '暂不撤销',
content: this.$lang('cancelRefund.content'),
cancelText: this.$lang('cancelRefund.cancelText'),
cancelColor: '#898989',
success: res => {
if (res.confirm) {

View File

@@ -14,8 +14,8 @@
</view>
<block v-if="g0>0">
<view class="foot">
<view @tap="generateImage" class="item" style="margin-right:30rpx;">{{ $lang('exportImage') }}</view>
<view @tap="generatePdf" class="item">{{ $lang('exportPdf') }}</view>
<view @tap="generateImage" class="item" style="margin-right:30rpx;">{{ $lang('seal.medium.detail.exportImage') }}</view>
<view @tap="generatePdf" class="item">{{ $lang('seal.medium.detail.exportPdf') }}</view>
</view>
</block>
</view>
@@ -39,7 +39,7 @@ export default {
}
},
onLoad(options) {
this.title = this.$lang('title');
this.title = this.$lang('seal.medium.detail.title');
if (options.keyword) {
this.keyword = options.keyword;
}
@@ -51,7 +51,7 @@ export default {
methods: {
generatePdf() {
const url = this.$util.img(this.info.files_url);
uni.showLoading({ title: this.$lang('loading') });
uni.showLoading({ title: this.$lang('seal.medium.detail.loading') });
try {
uni.downloadFile({
url: url,
@@ -71,7 +71,7 @@ export default {
},
complete: (res) => {
if (res.statusCode === 404) {
uni.showToast({ title: this.$lang('fileNotExist'), icon: 'none' });
uni.showToast({ title: this.$lang('seal.medium.detail.fileNotExist'), icon: 'none' });
}
}
});
@@ -81,7 +81,7 @@ export default {
},
generateImage() {
const url = this.$util.img(this.info.img_url);
uni.showLoading({ title: this.$lang('loading') });
uni.showLoading({ title: this.$lang('seal.medium.detail.loading') });
uni.downloadFile({
url: url,
success: (res) => {
@@ -90,21 +90,21 @@ export default {
uni.saveImageToPhotosAlbum({
filePath: res.tempFilePath,
success: () => {
this.$util.showToast({ title: this.$lang('saveSuccess') });
this.$util.showToast({ title: this.$lang('seal.medium.detail.saveSuccess') });
},
fail: (err) => {
if (err.errMsg !== 'saveImageToPhotosAlbum:fail auth deny' && err.errMsg !== 'saveImageToPhotosAlbum:fail:auth denied') {
this.$util.showToast({ title: this.$lang('saveFailed') });
this.$util.showToast({ title: this.$lang('seal.medium.detail.saveFailed') });
}
}
});
} else {
this.$util.showToast({ title: this.$lang('downloadFailed') });
this.$util.showToast({ title: this.$lang('seal.medium.detail.downloadFailed') });
}
},
fail: (err) => {
uni.hideLoading();
this.$util.showToast({ title: this.$lang('downloadFailed') });
this.$util.showToast({ title: this.$lang('seal.medium.detail.downloadFailed') });
}
});
},

View File

@@ -367,10 +367,12 @@ function main() {
return { text: result, inBlockComment };
}
// 判断一行是否是 $lang 调用(已经国际化)
// 判断一行是否是国际化调用(已经国际化)
function isLangCall(line) {
// 匹配 $lang(' 或 $lang("
return /\$lang\(\s*['"]/.test(line);
// 匹配 $lang(' 或 $lang(" 或 _t(' 或 _t(" 或 _getLang().lang(' 或 _getLang().lang("
return /\$lang\(\s*['"]/.test(line)
|| /_t\(\s*['"]/.test(line)
|| /_getLang\(\)\.lang\(\s*['"]/.test(line);
}
const hardcodeResults = [];