This commit is contained in:
wuyanchen 2025-11-16 19:31:55 +08:00
commit 318cae742e
184 changed files with 11604 additions and 0 deletions

4
.env.development Normal file
View File

@ -0,0 +1,4 @@
# .env.development
VITE_ENV = 'development'
VITE_API_URL = 'http://localhost:5005'
#VITE_API_URL = 'https://tankapi.xy88.fun:8180'

3
.env.production Normal file
View File

@ -0,0 +1,3 @@
# .env.production
VITE_ENV = 'production'
VITE_API_URL = 'https://tankapi.xy88.fun:8180'

24
.gitignore vendored Normal file
View File

@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

3
.vscode/extensions.json vendored Normal file
View File

@ -0,0 +1,3 @@
{
"recommendations": ["Vue.volar"]
}

3
README.md Normal file
View File

@ -0,0 +1,3 @@
# xprint.admin
XPrint 管理后台前端项目

13
index.html Normal file
View File

@ -0,0 +1,13 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title></title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>

4288
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

38
package.json Normal file
View File

@ -0,0 +1,38 @@
{
"name": "xprint.admin",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vue-tsc -b && vite build",
"preview": "vite preview"
},
"dependencies": {
"@babylonjs/core": "^8.27.0",
"@babylonjs/loaders": "^8.27.0",
"ali-oss": "^6.23.0",
"amfe-flexible": "^2.2.1",
"axios": "^1.11.0",
"element-plus": "^2.11.1",
"fabric": "^6.7.1",
"file-saver": "^2.0.5",
"imagetracerjs": "^1.2.6",
"save": "^2.9.0",
"vue": "^3.5.18",
"vue-loading-overlay": "^6.0.6",
"vue-router": "^4.5.1"
},
"devDependencies": {
"@types/ali-oss": "^6.16.11",
"@types/file-saver": "^2.0.7",
"@types/node": "^24.5.1",
"@vitejs/plugin-vue": "^6.0.1",
"@vue/tsconfig": "^0.7.0",
"less": "^4.4.1",
"simplify-img-compressor": "^1.0.4",
"typescript": "~5.8.3",
"vite": "^7.1.2",
"vue-tsc": "^3.0.5"
}
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 851 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 246 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 256 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 232 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 378 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 262 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 404 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 218 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 612 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 562 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 341 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 574 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 708 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 595 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 993 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 392 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 392 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 631 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 644 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 180 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 158 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 166 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 265 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 164 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 307 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 512 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

1
public/vite.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

32
src/App.vue Normal file
View File

@ -0,0 +1,32 @@
<script setup lang="ts">
</script>
<template>
<!-- <div>
<a href="https://vite.dev" target="_blank">
<img src="/vite.svg" class="logo" alt="Vite logo" />
</a>
<a href="https://vuejs.org/" target="_blank">
<img src="./assets/vue.svg" class="logo vue" alt="Vue logo" />
</a>
</div>
<HelloWorld msg="Vite + Vue" /> -->
<router-view></router-view>
</template>
<style scoped>
.logo {
height: 6em;
padding: 1.5em;
will-change: filter;
transition: filter 300ms;
}
.logo:hover {
filter: drop-shadow(0 0 2em #646cffaa);
}
.logo.vue:hover {
filter: drop-shadow(0 0 2em #42b883aa);
}
</style>

26
src/Global.ts Normal file
View File

@ -0,0 +1,26 @@
export class Global {
private static isInited: boolean = false;
public static selectedPrompt: string = "";
public static selectedImageOssInfo = {
objectName: "",
url: ""
};
public static spaceImagesDesc = new Array<string>();
public static spaceImages = new Array<any>();
public static maxProductImageSize = 1536; // 生产图最大图片尺寸(长边)
public static maxAiImageSize = 1536; // AI生成最大图片尺寸长边
public static init() {
if (Global.isInited) {
return;
}
Global.isInited = true;
}
public static selectImage(id: string) {
const imageInfo = Global.spaceImages.find((s) => s.id === id);
return imageInfo;
}
}
Global.init();

84
src/api/BaseApi.ts Normal file
View File

@ -0,0 +1,84 @@
import axios from "axios";
import { ElMessage } from "element-plus";
import { ToastTool } from "../tools/ToastTool";
export class BaseApi {
public static readonly baseUrl = import.meta.env.VITE_API_URL as string;
public maxTryRequestCount = 3; //最大重试请求次数
public tryDelayMs = 500; //重试延迟毫秒数
public static noTokenHandler: Function | null = null; //没有token时的处理函数,
public async requestRemoteWithoutToken(
option: any,
tryCount = 0
): Promise<any> {
option.hasHeader = false;
return await this.requestRemote(option, tryCount);
}
public async requestRemote(option: any, tryCount = 0): Promise<any> {
if (option.method == null) {
option.method = "get";
}
if (option.hasHeader == null) {
option.hasHeader = true;
}
let token = null;
if (option.hasHeader) {
const tokenStr = localStorage.getItem("token");
if (tokenStr == null) {
if (BaseApi.noTokenHandler != null) {
BaseApi.noTokenHandler();
}
return { data: null };
}
token = JSON.parse(tokenStr);
}
let scope = this;
// 发起一个get 或 post请求
let response = await axios({
method: option.method,
url: `${BaseApi.baseUrl}/${option.url}`,
data: option.method === "post" ? option.data : null,
params: option.method === "get" ? option.data : null,
headers: option.hasHeader
? {
"Content-Type": "application/json",
Authorization: `Bearer ${token.access_token}`,
}
: {
"Content-Type": "application/json",
},
}).catch((err: any) => {
console.error(err);
if (err.response != null && err.response.status === 401) {
ToastTool.showWarningMessage("登录信息过期,请重新登录");
if (BaseApi.noTokenHandler != null) {
BaseApi.noTokenHandler();
}
return null;
} else if (err.response != null && err.response.status === 500) {
ToastTool.showErrorMessage("500 InnerException");
return null;
} else {
tryCount++;
if (tryCount < scope.maxTryRequestCount) {
setTimeout(async () => {
return await scope.requestRemote(option, tryCount);
}, scope.tryDelayMs);
} else {
ToastTool.showErrorMessage("服务器维护中");
return null;
}
}
});
if (response != null) return response.data;
else return null;
}
}
const baseApi = new BaseApi();
export { baseApi };

53
src/api/LoginApi.ts Normal file
View File

@ -0,0 +1,53 @@
import { BaseApi } from "./BaseApi";
class LoginApi extends BaseApi {
public async login(user_name: string, password: string) {
const token = await this.requestRemoteWithoutToken({
method: "post",
url: "api/BackstageUser/LoginAdminAndRefreshToken",
data: {
user_name: user_name,
password: password,
},
});
if (token.resCode != 200) {
return null;
}
if (token.access_token == null) {
return null;
}
localStorage.setItem("token", JSON.stringify(token));
return token;
}
public checkLogin(): boolean {
let token = localStorage.getItem("token");
if (token == null) {
return false;
}
return true;
}
public async autoLogin(): Promise<boolean> {
let token = localStorage.getItem("token");
if (token != null) {
let isOK = await this.requestRemote({
method: "get",
url: "api/BackstageUser/CheckAuth",
});
return isOK;
// if (isOK) {
// this.$router.push({ path: '/main' })
// }
}
return false;
}
}
const loginApi = new LoginApi();
export { loginApi };

33
src/api/ModelApi.ts Normal file
View File

@ -0,0 +1,33 @@
import { BaseApi } from "./BaseApi";
class ModelApi extends BaseApi {
public async addOrUpdateModel(model: any): Promise<any> {
const data = await this.requestRemote({
method: "post",
url: "api/Model/AddOrUpdateModel",
data: model,
});
return data;
}
public async modelList(pageData: any): Promise<any> {
const data = await this.requestRemote({
method: "post",
url: "api/Model/ModelList",
data: pageData,
});
return data;
}
public async modelById(id: string): Promise<any> {
const data = await this.requestRemote({
method: "post",
url: "api/Model/ModelById",
data: id,
});
return data;
}
}
const modelApi = new ModelApi();
export { modelApi };

24
src/api/ModelPartApi.ts Normal file
View File

@ -0,0 +1,24 @@
import { BaseApi } from "./BaseApi";
class ModelPartApi extends BaseApi {
public async addOrUpdateModelParts(model: any): Promise<any> {
const data = await this.requestRemote({
method: "post",
url: "api/ModelPart/AddOrUpdateModelParts",
data: model,
});
return data;
}
public async modelPartsFormModelId(modelId: string): Promise<any> {
const data = await this.requestRemote({
method: "post",
url: "api/ModelPart/ModePartsFormModelId",
data: JSON.stringify(modelId),
});
return data;
}
}
const modelPartApi = new ModelPartApi();
export { modelPartApi };

119
src/api/OssApi.ts Normal file
View File

@ -0,0 +1,119 @@
import OSS from "ali-oss";
import { BaseApi } from "./BaseApi";
export class OssApi extends BaseApi {
public async getOssObjectName(url: string): Promise<string> {
const objectName = await this.requestRemote({
method: "post",
url: `api/Oss/GetOssObjectName`,
data: JSON.stringify(url),
});
return objectName;
}
public async deleteFileFromOss(objectName: string): Promise<any> {
const info = await this.requestRemote({
method: "post",
url: "api/Oss/GetOssInfo",
});
if (!info) {
return null;
}
const scope = this;
const store = new OSS({
accessKeyId: info.token.accessKeyId,
accessKeySecret: info.token.accessKeySecret,
stsToken: info.token.securityToken,
bucket: info.bucket,
secure: true,
endpoint: info.endpoint,
refreshSTSToken: async () => {
const info = await scope.requestRemote({
method: "post",
url: "api/Oss/GetOssInfo",
});
if (info == null) {
return {
accessKeyId: "",
accessKeySecret: "",
stsToken: "",
secure: true,
bucket: "",
endpoint: "",
};
}
return {
accessKeyId: info.token.accessKeyId,
accessKeySecret: info.token.accessKeySecret,
stsToken: info.token.securityToken,
secure: true,
bucket: info.bucket,
endpoint: info.endpoint,
};
},
refreshSTSTokenInterval: 300000,
});
let result = await store.delete(objectName);
return result;
}
public async uploadFileToOss(
objectName: string,
blob: Blob,
expiresAt: string | null = null
): Promise<any> {
const info = await this.requestRemote({
method: "post",
url: "api/Oss/GetOssInfo",
});
if (!info) {
return null;
}
const scope = this;
const store = new OSS({
accessKeyId: info.token.accessKeyId,
accessKeySecret: info.token.accessKeySecret,
stsToken: info.token.securityToken,
bucket: info.bucket,
secure: true,
endpoint: info.endpoint,
refreshSTSToken: async () => {
const info = await scope.requestRemote({
method: "post",
url: "api/Oss/GetOssInfo",
});
if (info == null) {
return {
accessKeyId: "",
accessKeySecret: "",
stsToken: "",
secure: true,
bucket: "",
endpoint: "",
};
}
return {
accessKeyId: info.token.accessKeyId,
accessKeySecret: info.token.accessKeySecret,
stsToken: info.token.securityToken,
secure: true,
bucket: info.bucket,
endpoint: info.endpoint,
};
},
refreshSTSTokenInterval: 300000,
});
let headers = undefined;
if (expiresAt) {
headers = {
Expires: expiresAt,
};
}
let result = await store.put(objectName, blob, {
headers: headers,
});
return result;
}
}
const ossApi = new OssApi();
export { ossApi };

77
src/api/dto/ModelPart.ts Normal file
View File

@ -0,0 +1,77 @@
export class ModelPart {
public id: string = "";
public modelId: string = "";
/**
*
*/
public partKey: string = "";
/**
*
*/
public partName: string = "";
/**
*
*/
public maskImageUrl: string = "";
public uvx: number = 0;
public uvy: number = 0;
public uvWidth: number = 0;
public uvHeight: number = 0;
public productImageX: number = 0;
public productImageY: number = 0;
public productImageWidth: number = 0;
public productImageHeight: number = 0;
public partColor: string = "#FFFFFF";
/**
*
*/
public isCanEditPicture: boolean = false;
/**
*
*/
public isCanEditColor: boolean = false;
/**
*
*/
public sort: number = 0;
/**
* Alpha值
*/
public alpha: number = 0;
/**
* Beta值
*/
public beta: number = 0;
/**
* Radius值
*/
public radius: number = 0;
/**
* Alpha值js number
*/
public alphaStr: string = "0";
/**
* Beta值js number
*/
public betaStr: string = "0";
/**
* Radius值js number
*/
public radiusStr: string = "0";
/**
*
*/
public maskScale: number = 1;
constructor() {}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

BIN
src/assets/img/bg.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

BIN
src/assets/img/close.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 168 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 168 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Some files were not shown because too many files have changed in this diff Show More