Compare commits
3 Commits
a2b45e2041
...
research
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ae1d7245f4 | ||
|
|
b8f38db351 | ||
|
|
7fb7ffaced |
@@ -55,7 +55,6 @@ commandManager.setChangeCallback((info) => {
|
|||||||
emit("undo-redo-status-changed", {
|
emit("undo-redo-status-changed", {
|
||||||
canUndo: canUndo.value,
|
canUndo: canUndo.value,
|
||||||
canRedo: canRedo.value,
|
canRedo: canRedo.value,
|
||||||
type: info.type,
|
|
||||||
commandManager,
|
commandManager,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -907,8 +907,7 @@
|
|||||||
}
|
}
|
||||||
emit("changeCanvas", commandData)
|
emit("changeCanvas", commandData)
|
||||||
canvasManager.changeCanvas()
|
canvasManager.changeCanvas()
|
||||||
const type = command.type
|
if ((command.canUndo || command.canRedo) && props.enabledRedGreenMode) {
|
||||||
if (props.enabledRedGreenMode && (type === "undo" || type === "redo")) {
|
|
||||||
setTimeout(async () => {
|
setTimeout(async () => {
|
||||||
try {
|
try {
|
||||||
const imageData = await canvasManager.exportImage({
|
const imageData = await canvasManager.exportImage({
|
||||||
@@ -1041,7 +1040,6 @@
|
|||||||
},
|
},
|
||||||
// 导出图片
|
// 导出图片
|
||||||
exportImage: async ({
|
exportImage: async ({
|
||||||
isFrontBackUpdata = false, // 更新红绿图时候需要用,直接更新红绿图
|
|
||||||
isContainBg = false, // 是否包含背景图层
|
isContainBg = false, // 是否包含背景图层
|
||||||
isContainFixed = false, // 是否包含固定图层
|
isContainFixed = false, // 是否包含固定图层
|
||||||
isContainFixedOther = true, // 是否包含其他固定图层--颜色图层
|
isContainFixedOther = true, // 是否包含其他固定图层--颜色图层
|
||||||
@@ -1058,10 +1056,6 @@
|
|||||||
} = {}) => {
|
} = {}) => {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
canvasManager?.canvas?.discardActiveObject()
|
canvasManager?.canvas?.discardActiveObject()
|
||||||
if (isFrontBackUpdata) {
|
|
||||||
await canvasManager?.setSpecialCliptInfo(true, true)
|
|
||||||
canvasManager.canvas.renderAll()
|
|
||||||
}
|
|
||||||
var base64 = await canvasManager.exportImage({
|
var base64 = await canvasManager.exportImage({
|
||||||
isContainBg,
|
isContainBg,
|
||||||
isContainFixed,
|
isContainFixed,
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -180,7 +180,7 @@ export class CommandManager {
|
|||||||
this._recordPerformance("execute", command.constructor.name, duration);
|
this._recordPerformance("execute", command.constructor.name, duration);
|
||||||
|
|
||||||
// 通知状态变化
|
// 通知状态变化
|
||||||
this._notifyStateChange("execute");
|
this._notifyStateChange();
|
||||||
|
|
||||||
console.log(`✅ 命令执行成功: ${command.constructor.name}`);
|
console.log(`✅ 命令执行成功: ${command.constructor.name}`);
|
||||||
return result;
|
return result;
|
||||||
@@ -219,7 +219,7 @@ export class CommandManager {
|
|||||||
this._recordPerformance("undo", command.constructor.name, duration);
|
this._recordPerformance("undo", command.constructor.name, duration);
|
||||||
|
|
||||||
// 通知状态变化
|
// 通知状态变化
|
||||||
this._notifyStateChange("undo");
|
this._notifyStateChange();
|
||||||
|
|
||||||
console.log(`✅ 命令撤销成功: ${command.constructor.name}`);
|
console.log(`✅ 命令撤销成功: ${command.constructor.name}`);
|
||||||
return result;
|
return result;
|
||||||
@@ -258,7 +258,7 @@ export class CommandManager {
|
|||||||
this._recordPerformance("redo", command.constructor.name, duration);
|
this._recordPerformance("redo", command.constructor.name, duration);
|
||||||
|
|
||||||
// 通知状态变化
|
// 通知状态变化
|
||||||
this._notifyStateChange("redo");
|
this._notifyStateChange();
|
||||||
|
|
||||||
console.log(`✅ 命令重做成功: ${command.constructor.name}`);
|
console.log(`✅ 命令重做成功: ${command.constructor.name}`);
|
||||||
return result;
|
return result;
|
||||||
@@ -298,7 +298,7 @@ export class CommandManager {
|
|||||||
|
|
||||||
this.undoStack = [];
|
this.undoStack = [];
|
||||||
this.redoStack = [];
|
this.redoStack = [];
|
||||||
this._notifyStateChange("clear");
|
this._notifyStateChange();
|
||||||
// console.log("📝 命令历史已清空");
|
// console.log("📝 命令历史已清空");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -417,12 +417,10 @@ export class CommandManager {
|
|||||||
* 通知状态变化
|
* 通知状态变化
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_notifyStateChange(type) {
|
_notifyStateChange() {
|
||||||
if (this.onStateChange) {
|
if (this.onStateChange) {
|
||||||
try {
|
try {
|
||||||
const obj = this.getState();
|
this.onStateChange(this.getState());
|
||||||
obj.type = type;
|
|
||||||
this.onStateChange(obj);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("状态变化回调执行失败:", error);
|
console.error("状态变化回调执行失败:", error);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ export class LiquifyRealTimeUpdater {
|
|||||||
|
|
||||||
if (isDrawing && this.config.useDirectUpdate) {
|
if (isDrawing && this.config.useDirectUpdate) {
|
||||||
// 拖拽过程中使用快速更新(降低质量以提高性能)
|
// 拖拽过程中使用快速更新(降低质量以提高性能)
|
||||||
await this._fastUpdate(imageData);
|
this._fastUpdate(imageData);
|
||||||
} else {
|
} else {
|
||||||
// 拖拽结束后使用完整更新(最高质量)
|
// 拖拽结束后使用完整更新(最高质量)
|
||||||
await this._fullUpdate(imageData);
|
await this._fullUpdate(imageData);
|
||||||
@@ -124,7 +124,7 @@ export class LiquifyRealTimeUpdater {
|
|||||||
* @param {ImageData} imageData 图像数据
|
* @param {ImageData} imageData 图像数据
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
async _fastUpdate(imageData) {
|
_fastUpdate(imageData) {
|
||||||
if (!this.targetObject || !this.targetObject._element) {
|
if (!this.targetObject || !this.targetObject._element) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -138,14 +138,12 @@ export class LiquifyRealTimeUpdater {
|
|||||||
|
|
||||||
// 直接更新fabric对象的图像源(使用PNG格式保持质量)
|
// 直接更新fabric对象的图像源(使用PNG格式保持质量)
|
||||||
const targetElement = this.targetObject._element;
|
const targetElement = this.targetObject._element;
|
||||||
|
|
||||||
// 方案1: 直接设置src属性(最高性能)
|
// 方案1: 直接设置src属性(最高性能)
|
||||||
const dataURL = this.tempCanvas.toDataURL("image/png", quality);
|
const dataURL = this.tempCanvas.toDataURL("image/png", quality);
|
||||||
|
|
||||||
if (targetElement.src !== dataURL) {
|
if (targetElement.src !== dataURL) {
|
||||||
// targetElement.src = dataURL;
|
targetElement.src = dataURL;
|
||||||
const image = new Image();
|
|
||||||
image.src = dataURL;
|
|
||||||
await image.decode();
|
|
||||||
this.targetObject.setElement(image);
|
|
||||||
|
|
||||||
// 关键优化:直接设置fabric对象为脏状态,但不立即渲染
|
// 关键优化:直接设置fabric对象为脏状态,但不立即渲染
|
||||||
// this.targetObject.dirty = false; // 标记为不需要立即渲染
|
// this.targetObject.dirty = false; // 标记为不需要立即渲染
|
||||||
|
|||||||
@@ -447,7 +447,6 @@
|
|||||||
</template>
|
</template>
|
||||||
</CanvasEditor>
|
</CanvasEditor>
|
||||||
</div>
|
</div>
|
||||||
<img src="" alt="" id="canvas-test-dom">
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<style>
|
<style>
|
||||||
@@ -459,13 +458,6 @@
|
|||||||
height: 600px !important;
|
height: 600px !important;
|
||||||
z-index: 99999999;
|
z-index: 99999999;
|
||||||
}
|
}
|
||||||
#canvas-test-dom{
|
|
||||||
position: fixed;
|
|
||||||
z-index: 9999999999;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
<style scoped lang="less">
|
<style scoped lang="less">
|
||||||
* {
|
* {
|
||||||
|
|||||||
@@ -163,6 +163,7 @@ export default defineComponent({
|
|||||||
const privewDetail = async (oldSelectDetail = detailData.selectDetail)=>{
|
const privewDetail = async (oldSelectDetail = detailData.selectDetail)=>{
|
||||||
// if(!detailDom.editCanvas)return
|
// if(!detailDom.editCanvas)return
|
||||||
return new Promise(async (res,reject)=>{
|
return new Promise(async (res,reject)=>{
|
||||||
|
console.log(detailDom.editCanvas)
|
||||||
await detailDom.editCanvas.exportImage({
|
await detailDom.editCanvas.exportImage({
|
||||||
isContainFixed:true,
|
isContainFixed:true,
|
||||||
width:props.sketchSize.width,
|
width:props.sketchSize.width,
|
||||||
@@ -244,27 +245,17 @@ export default defineComponent({
|
|||||||
|
|
||||||
|
|
||||||
const frontBackChange = async (value:any)=>{
|
const frontBackChange = async (value:any)=>{
|
||||||
let front = detailData.frontBack.front[detailData.imgDomIndex]
|
|
||||||
let back = detailData.frontBack.back[detailData.imgDomIndex]
|
|
||||||
store.commit('DesignDetail/updataDetailItem',{maskUrl:value})
|
|
||||||
await nextTick()
|
|
||||||
if(!detailData.selectDetail.partialDesign.partialDesignPath && !detailData.selectDetail.partialDesign.partialDesignBase64){
|
if(!detailData.selectDetail.partialDesign.partialDesignPath && !detailData.selectDetail.partialDesign.partialDesignBase64){
|
||||||
await privewDetail()
|
await privewDetail()
|
||||||
}else{
|
|
||||||
await detailDom.editCanvas.exportImage({
|
|
||||||
isFrontBackUpdata: true,
|
|
||||||
isContainFixed:true,
|
|
||||||
width:props.sketchSize.width,
|
|
||||||
height:props.sketchSize.height,
|
|
||||||
}).then((rv)=>{
|
|
||||||
if(detailData.selectDetail?.partialDesign)detailData.selectDetail.partialDesign.partialDesignBase64 = rv
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
let full = detailData.selectDetail.partialDesign.partialDesignBase64 || detailData.selectDetail.partialDesign.partialDesignPath || detailData.selectDetail.path
|
let full = detailData.selectDetail.partialDesign.partialDesignBase64 || detailData.selectDetail.partialDesign.partialDesignPath || detailData.selectDetail.path
|
||||||
let size = {
|
let size = {
|
||||||
...detailData.canvasConfig,
|
...detailData.canvasConfig,
|
||||||
}
|
}
|
||||||
|
store.commit('DesignDetail/updataDetailItem',{maskUrl:value})
|
||||||
segmentImage(value,full,size).then(async (rv)=>{
|
segmentImage(value,full,size).then(async (rv)=>{
|
||||||
|
let front = detailData.frontBack.front[detailData.imgDomIndex]
|
||||||
|
let back = detailData.frontBack.back[detailData.imgDomIndex]
|
||||||
if(!front?.oldImageUrl)front.oldImageUrl = front.imageUrl
|
if(!front?.oldImageUrl)front.oldImageUrl = front.imageUrl
|
||||||
if(!front?.oldMaskUrl)front.oldMaskUrl = front.maskUrl
|
if(!front?.oldMaskUrl)front.oldMaskUrl = front.maskUrl
|
||||||
if(!back?.oldImageUrl)back.oldImageUrl = back.imageUrl
|
if(!back?.oldImageUrl)back.oldImageUrl = back.imageUrl
|
||||||
@@ -276,6 +267,7 @@ export default defineComponent({
|
|||||||
back.imageUrl = rv.targetBackUrl
|
back.imageUrl = rv.targetBackUrl
|
||||||
// store.commit('DesignDetail/updataDetailItem',{maskUrl:value})
|
// store.commit('DesignDetail/updataDetailItem',{maskUrl:value})
|
||||||
})
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
const editSketchCanvasInit = async (value:any)=>{
|
const editSketchCanvasInit = async (value:any)=>{
|
||||||
detailData.canvasInstance = value
|
detailData.canvasInstance = value
|
||||||
|
|||||||
@@ -393,8 +393,8 @@ export default defineComponent({
|
|||||||
angle: 0,
|
angle: 0,
|
||||||
flipX: false,
|
flipX: false,
|
||||||
flipY: false,
|
flipY: false,
|
||||||
blendMode: "multiply",
|
// blendMode: "multiply",
|
||||||
// blendMode: "source-over",
|
blendMode: "source-over",
|
||||||
gapX: 0,
|
gapX: 0,
|
||||||
gapY: 0,
|
gapY: 0,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -531,7 +531,7 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
const setSpeed = (item: any) => {
|
const setSpeed = (item: any) => {
|
||||||
speed.speedData = {...item}
|
speed.speedData = item
|
||||||
}
|
}
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
if (props.msg == 'Sketchboard') {
|
if (props.msg == 'Sketchboard') {
|
||||||
|
|||||||
@@ -205,7 +205,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="payment">
|
<div class="payment">
|
||||||
<div class="allocation">
|
<div class="allocation">
|
||||||
<!-- <div class="selectType">
|
<div class="selectType">
|
||||||
<div class="text">{{ $t('Renew.Payment') }}:</div>
|
<div class="text">{{ $t('Renew.Payment') }}:</div>
|
||||||
<label>
|
<label>
|
||||||
<input
|
<input
|
||||||
@@ -227,7 +227,7 @@
|
|||||||
/>
|
/>
|
||||||
{{ $t('Renew.Alipay') }}
|
{{ $t('Renew.Alipay') }}
|
||||||
</label>
|
</label>
|
||||||
</div> -->
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="gallery_btn gallery_btn_radius" @click="payment">
|
<div class="gallery_btn gallery_btn_radius" @click="payment">
|
||||||
{{ $t('upgradePlan.Continue') }}
|
{{ $t('upgradePlan.Continue') }}
|
||||||
|
|||||||
@@ -77,10 +77,17 @@ export default defineComponent({
|
|||||||
left: 50%;
|
left: 50%;
|
||||||
top: 50%;
|
top: 50%;
|
||||||
transform: translate(-50%,-50%);
|
transform: translate(-50%,-50%);
|
||||||
|
width: 80%;
|
||||||
|
height: auto;
|
||||||
|
max-height: 80vh;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
width: max-content;
|
||||||
video{
|
video{
|
||||||
max-height:80vh;
|
width: 100%;
|
||||||
max-width:80vw;
|
max-height: 80vh;
|
||||||
|
height: 100%;
|
||||||
|
object-fit: contain;
|
||||||
|
width: max-content;
|
||||||
}
|
}
|
||||||
.general_video_btn{
|
.general_video_btn{
|
||||||
color: #fff;
|
color: #fff;
|
||||||
|
|||||||
@@ -41,10 +41,10 @@
|
|||||||
<div class="center">{{ selectObject?.styleName?selectObject?.styleName:$t('Header.All') }}</div>
|
<div class="center">{{ selectObject?.styleName?selectObject?.styleName:$t('Header.All') }}</div>
|
||||||
<div class="gallery_btn" @click="setStyle">{{ $t('Habit.Select') }}</div>
|
<div class="gallery_btn" @click="setStyle">{{ $t('Habit.Select') }}</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- <div class="style brand marginBottom">
|
<div class="style brand marginBottom">
|
||||||
<div class="text">{{$t('Habit.Brand')}}:</div>
|
<div class="text">{{$t('Habit.Brand')}}:</div>
|
||||||
<div class="gallery_btn" @click="setBrandDNA">{{ $t('Habit.Select') }}</div>
|
<div class="gallery_btn" @click="setBrandDNA">{{ $t('Habit.Select') }}</div>
|
||||||
</div> -->
|
</div>
|
||||||
<div class="brandImg" v-if="selectObject.userBrandDna"><img :src="selectObject.userBrandDnaImg"></div>
|
<div class="brandImg" v-if="selectObject.userBrandDna"><img :src="selectObject.userBrandDnaImg"></div>
|
||||||
<div class="brandDNAStrenght marginBottom" v-if="selectObject.userBrandDna">
|
<div class="brandDNAStrenght marginBottom" v-if="selectObject.userBrandDna">
|
||||||
<div class="text" style="font-size: 1.6rem;">
|
<div class="text" style="font-size: 1.6rem;">
|
||||||
|
|||||||
@@ -178,12 +178,6 @@ const routes: Array<RouteRecordRaw> = [
|
|||||||
meta: { enter: "all" },
|
meta: { enter: "all" },
|
||||||
component: () => import("@/views/HomeRecommend.vue"),
|
component: () => import("@/views/HomeRecommend.vue"),
|
||||||
},
|
},
|
||||||
{
|
|
||||||
path: "/Square/:lang",
|
|
||||||
name: "HomeRecommendLang",
|
|
||||||
meta: { enter: "all" },
|
|
||||||
component: () => import("@/views/HomeRecommend.vue"),
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
path: "/administrator",
|
path: "/administrator",
|
||||||
name: "administrator",
|
name: "administrator",
|
||||||
|
|||||||
@@ -272,12 +272,12 @@ const navTypeList = (t)=>{
|
|||||||
// },
|
// },
|
||||||
|
|
||||||
|
|
||||||
// {
|
{
|
||||||
// icon:'fi fi-rr-puzzle-alt',
|
icon:'fi fi-rr-puzzle-alt',
|
||||||
// value:'deReconstruction',
|
value:'deReconstruction',
|
||||||
// label:t('Header.toolsDeReconstruction'),
|
label:t('Header.toolsDeReconstruction'),
|
||||||
// router:'tools=deReconstruction'
|
router:'tools=deReconstruction'
|
||||||
// },
|
},
|
||||||
{
|
{
|
||||||
icon:'fi fi-ss-box-open',
|
icon:'fi fi-ss-box-open',
|
||||||
value:'toProduct',
|
value:'toProduct',
|
||||||
@@ -294,18 +294,18 @@ const navTypeList = (t)=>{
|
|||||||
label:t('Header.toolsToTransferPose'),
|
label:t('Header.toolsToTransferPose'),
|
||||||
router:'tools=poseTransfer'
|
router:'tools=poseTransfer'
|
||||||
},
|
},
|
||||||
// {
|
{
|
||||||
// icon:'fi fi-rr-cubes',
|
icon:'fi fi-rr-cubes',
|
||||||
// value:'patternMaking3D',
|
value:'patternMaking3D',
|
||||||
// label:t('Header.toolsPatternMaking'),
|
label:t('Header.toolsPatternMaking'),
|
||||||
// router:'tools=patternMaking3D'
|
router:'tools=patternMaking3D'
|
||||||
// },
|
},
|
||||||
// {
|
{
|
||||||
// icon:'fi fi-rr-pen-swirl',
|
icon:'fi fi-rr-pen-swirl',
|
||||||
// value:'canvasUpload',
|
value:'canvasUpload',
|
||||||
// label:t('Header.toolsCanvas'),
|
label:t('Header.toolsCanvas'),
|
||||||
// router:'tools=canvasUpload'
|
router:'tools=canvasUpload'
|
||||||
// },
|
},
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
library:{
|
library:{
|
||||||
@@ -340,12 +340,12 @@ const navTypeList = (t)=>{
|
|||||||
value:'Models',
|
value:'Models',
|
||||||
router:'library=Models'
|
router:'library=Models'
|
||||||
},
|
},
|
||||||
// {
|
{
|
||||||
// icon:'fi-ss-gem',
|
icon:'fi-ss-gem',
|
||||||
// label:t('LibraryPage.brandDNA'),
|
label:t('LibraryPage.brandDNA'),
|
||||||
// value:'MyBrand',
|
value:'MyBrand',
|
||||||
// router:'library=MyBrand'
|
router:'library=MyBrand'
|
||||||
// },
|
},
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
// history:{
|
// history:{
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ import { ExclamationCircleOutlined } from '@ant-design/icons-vue'
|
|||||||
import { useStore } from 'vuex'
|
import { useStore } from 'vuex'
|
||||||
import { setLang } from '@/tool/guide'
|
import { setLang } from '@/tool/guide'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
import { useRouter, useRoute } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
import { gsap, TweenMax } from 'gsap'
|
import { gsap, TweenMax } from 'gsap'
|
||||||
import { ScrollTrigger } from 'gsap/ScrollTrigger'
|
import { ScrollTrigger } from 'gsap/ScrollTrigger'
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
@@ -71,7 +71,6 @@ export default defineComponent({
|
|||||||
const {t, locale} = useI18n()
|
const {t, locale} = useI18n()
|
||||||
const store = useStore()
|
const store = useStore()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const route = useRoute()
|
|
||||||
let registerModel = ref()
|
let registerModel = ref()
|
||||||
let data = reactive({})
|
let data = reactive({})
|
||||||
|
|
||||||
@@ -118,14 +117,7 @@ export default defineComponent({
|
|||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
window.addEventListener('resize', updataIsMoblie)
|
window.addEventListener('resize', updataIsMoblie)
|
||||||
// 初始化语言设置
|
// 初始化语言设置
|
||||||
let savedLang = localStorage.getItem('loginLanguage')
|
const savedLang = localStorage.getItem('loginLanguage')
|
||||||
if(route?.params?.lang == 'cn'){
|
|
||||||
savedLang = 'CHINESE_SIMPLIFIED'
|
|
||||||
localStorage.setItem('loginLanguage', savedLang)
|
|
||||||
}else if(route?.params?.lang == 'en'){
|
|
||||||
savedLang = 'ENGLISH'
|
|
||||||
localStorage.setItem('loginLanguage', savedLang)
|
|
||||||
}
|
|
||||||
if (savedLang) {
|
if (savedLang) {
|
||||||
isChinese.value = savedLang === 'CHINESE_SIMPLIFIED'
|
isChinese.value = savedLang === 'CHINESE_SIMPLIFIED'
|
||||||
locale.value = savedLang
|
locale.value = savedLang
|
||||||
|
|||||||
Reference in New Issue
Block a user