commit 61b79f6528ee08189247f7642ac57567906e3f0d Author: 编码猿 Date: Tue Oct 7 20:17:19 2025 +0800 first commit diff --git a/.env.development b/.env.development new file mode 100644 index 0000000..3452635 --- /dev/null +++ b/.env.development @@ -0,0 +1,13 @@ +# 请求协议 http or https(建议线上配置证书后,使用https) +VITE_PREFIX=http:// + +# Nacos 配置中心 ip:端口 | 域名 +VITE_NACOS_ADDRESS=cluster.nacos.com +#VITE_NACOS_ADDRESS=192.168.139.84:8848 + + +# 注册到 Nacos 的服务名称(微前端模块名称) +VITE_SERVER_NAME=app-shared + +# 注册到 Nacos 的端口,也是前端运行的端口 +VITE_SERVER_PORT=1333 diff --git a/.env.production b/.env.production new file mode 100644 index 0000000..d69ebd5 --- /dev/null +++ b/.env.production @@ -0,0 +1,12 @@ +# 请求协议 http or https(建议线上配置证书后,使用https) +VITE_PREFIX=http:// + +# Nacos 配置中心 ip:端口 | 域名 +#VITE_NACOS_ADDRESS=cluster.nacos.com +VITE_NACOS_ADDRESS=192.168.139.84:8848 + +# 注册到 Nacos 的服务名称(微前端模块名称) +VITE_SERVER_NAME=app-shared + +# 注册到 Nacos 的端口,也是前端运行的端口 +VITE_SERVER_PORT=1333 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..688b875 --- /dev/null +++ b/.gitignore @@ -0,0 +1,27 @@ +.DS_Store +/node_modules/ +dist + +pnpm-lock.yaml +package-lock.json + +# local env files +.env.local +.env.*.local + +# Log files +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* + + +# Editor directories and files +.idea +.vscode +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? + diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..6a3f99d --- /dev/null +++ b/Dockerfile @@ -0,0 +1,21 @@ +FROM node:24-alpine AS builder +WORKDIR /app + +COPY package.json ./ + +# 直接使用 npm install 并解决依赖问题 +RUN npm install --no-audit --no-fund --legacy-peer-deps + +# 拷贝源码并构建 +COPY . . + +# 【前端修改】如果要以子路径 /app-shared/ 部署,请用 base 指定前缀,同 .env中VITE_SERVER_NAME +RUN npx vite build --base=/app-shared/ + +FROM nginx:alpine + +COPY --from=builder /app/dist /usr/share/nginx/html/app-shared +# 覆盖 Nginx 配置,支持 SPA 子路径 +COPY nginx.conf /etc/nginx/conf.d/default.conf +EXPOSE 80 +CMD ["nginx", "-g", "daemon off;"] \ No newline at end of file diff --git a/act-runner.yml b/act-runner.yml new file mode 100644 index 0000000..c0bc8bc --- /dev/null +++ b/act-runner.yml @@ -0,0 +1,17 @@ +version: '3.8' + +services: + gitea-actions-runner: + image: gitea/act_runner:latest + container_name: shared-act-runner + restart: unless-stopped + volumes: + - runner-vol:/data + - /var/run/docker.sock:/var/run/docker.sock + environment: + - GITEA_INSTANCE_URL=https://git.bmycode.help + - GITEA_RUNNER_REGISTRATION_TOKEN=HReAwXfHVogolZk43yt380s7inKtoeHv0C0lVBt1 + - GITEA_RUNNER_LABELS=ubuntu-latest:docker://docker.gitea.com/runner-images:ubuntu-latest + +volumes: + runner-vol: diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..89d904f --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,13 @@ +version: "3.8" + +services: + app-product: + build: + context: .. + dockerfile: docker/Dockerfile + + image: registry.bmycode.help/app-shared:latest + container_name: app-product + ports: + - "1301:80" + restart: unless-stopped diff --git a/index.html b/index.html new file mode 100644 index 0000000..baaf938 --- /dev/null +++ b/index.html @@ -0,0 +1,16 @@ + + + + + + + + Vite App + + + +
+ + + + \ No newline at end of file diff --git a/nginx.conf b/nginx.conf new file mode 100644 index 0000000..82195bf --- /dev/null +++ b/nginx.conf @@ -0,0 +1,14 @@ +server { + listen 80; + server_name *.ddd.com; + + + location /app-shared/ { + alias /usr/share/nginx/html/app-shared/; + try_files $uri $uri/ /app-shared/index.html; + add_header 'Access-Control-Allow-Origin' '*'; + add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS'; + add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range'; + add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range'; + } +} \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..dbf63dd --- /dev/null +++ b/package.json @@ -0,0 +1,30 @@ +{ + "name": "app-shared", + "version": "1.0.0", + "description": "", + "keywords": [], + "author": "", + "license": "ISC", + "scripts": { + "dev": "vite", + "dep": "concurrently \"vite build --watch\" \"npm run preview\"", + "preview": "vite preview --port 1333 --strictPort", + "build": "vite build", + "type-check": "vue-tsc --build" + }, + "dependencies": { + "@element-plus/icons-vue": "^2.3.2", + "axios": "^1.12.2", + "element-plus": "^2.11.4", + "vite-plugin-nacos": "^1.1.3", + "vue": "^3.5.22" + }, + "devDependencies": { + "@vitejs/plugin-vue": "^6.0.1", + "@vitejs/plugin-vue-jsx": "^5.1.1", + "rollup-plugin-visualizer": "^6.0.3", + "typescript": "~5.9.3", + "vite": "^7.1.7", + "vite-federation": "^3.4.4" + } +} \ No newline at end of file diff --git a/src/App.tsx b/src/App.tsx new file mode 100644 index 0000000..58b96fd --- /dev/null +++ b/src/App.tsx @@ -0,0 +1,24 @@ +import { defineComponent, onMounted } from 'vue' + +export default defineComponent({ + setup() { + + onMounted({}) + return () => ( + <> +
+

共享资源-联邦模块

+

此页面仅用于 商品模块 的单独开发和调试,可临时引入组件进行本地测试,实际运行时由 主应用 + 加载本项目代码

+ + {/*

本模块包含的功能:

*/ } + {/**/ } + {/*

*/ } +
+ + ) + } +}) \ No newline at end of file diff --git a/src/config/HttpConfig.ts b/src/config/HttpConfig.ts new file mode 100644 index 0000000..71df265 --- /dev/null +++ b/src/config/HttpConfig.ts @@ -0,0 +1,9 @@ +export class HttpConfig { + // public static DevBaseUrl: string = "http://127.0.0.1:3210/api" + public static DevBaseUrl: string = window.__APP_CONFIG__.VITE_DEVBASEURL + + // public static ProdBaseUrl: string = "http://127.0.0.1:3210/api/" + public static ProdBaseUrl: string = window.__APP_CONFIG__.VITE_PRODBASEURL + + public static apiList: string = "" +} diff --git a/src/config/ThemeConfig.ts b/src/config/ThemeConfig.ts new file mode 100644 index 0000000..7e08a8d --- /dev/null +++ b/src/config/ThemeConfig.ts @@ -0,0 +1,4 @@ +export class ThemeConfig { + public static primaryColor: string = '#1890ff'; // 主色调 + public static layoutType: 'side' | 'top' = 'side'; // 侧边栏布局 +} \ No newline at end of file diff --git a/src/config/index.ts b/src/config/index.ts new file mode 100644 index 0000000..75b3ffc --- /dev/null +++ b/src/config/index.ts @@ -0,0 +1,2 @@ +export * from './HttpConfig'; +export * from './ThemeConfig'; \ No newline at end of file diff --git a/src/constants/README.md b/src/constants/README.md new file mode 100644 index 0000000..abfa02f --- /dev/null +++ b/src/constants/README.md @@ -0,0 +1 @@ +# 临时占位,上传空文件夹,保存文件结构用,后面删除 \ No newline at end of file diff --git a/src/exceptions/README.md b/src/exceptions/README.md new file mode 100644 index 0000000..abfa02f --- /dev/null +++ b/src/exceptions/README.md @@ -0,0 +1 @@ +# 临时占位,上传空文件夹,保存文件结构用,后面删除 \ No newline at end of file diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..30a4190 --- /dev/null +++ b/src/index.ts @@ -0,0 +1,3 @@ +export * from './infra'; +export * from './utils'; +export * from './config'; diff --git a/src/infra/AxiosInfra.ts b/src/infra/AxiosInfra.ts new file mode 100644 index 0000000..cc37d90 --- /dev/null +++ b/src/infra/AxiosInfra.ts @@ -0,0 +1,98 @@ +import axios, { + AxiosError, + type AxiosInstance, + type AxiosRequestConfig, + AxiosResponse, + InternalAxiosRequestConfig +} from "axios"; +import { UrlCheckUtils } from '../utils/index' + +class AxiosInfra { + public instance: AxiosInstance; + + public constructor() { + this.instance = axios.create({ + baseURL: UrlCheckUtils.check(), + timeout: Number(window.__APP_CONFIG__.VITE_HTTPTIMEOUT), + headers: { + "Content-Type": "application/json" + } + }); + this.ResponseInterceptor(); + this.RequestInterceptor(); + } + + public async get(params: AxiosRequestConfig): Promise { + return await this.instance.get(params.url, { + params: params.data, + headers: Object.assign({ Authorization: localStorage.getItem("token") }, params.headers) + }); + } + + public async post(params: AxiosRequestConfig): Promise { + return await this.instance.post(params.url, params.data, { + headers: Object.assign({ Authorization: localStorage.getItem("token") }, params.headers) + }) + } + + /** + * Put 请求 + * 请求参数请参考接口:RequestParams + * @param params + */ + public async put(params: AxiosRequestConfig): Promise { + return await this.instance.put(params.url, params.data) + } + + /** + * delete 请求 + * 请求参数请参考接口:RequestParams + * @param params + */ + public async delete(params: AxiosRequestConfig): Promise { + return await this.instance.delete(params.url, { params: params.data }) + } + + /** + * 响应拦截器 + * @constructor + */ + public async ResponseInterceptor(): Promise { + this.instance.interceptors.response.use( + (response: AxiosResponse) => { + return response.data.result; + }, + (error: AxiosError) => { + console.log(` + ❌ 请求错误 ❌ + 1、基地址: ${ error.config?.baseURL } + 2、短地址: ${ error.config?.url } + 3、请求 方式: ${ error.config?.method } + 4、请求 参数: ${ JSON.stringify(error.config?.params) } + 5、请求 头 : ${ JSON.stringify(error.config?.headers) } + + ------------------------------------------------------------ + + 6、错误信息为: ${ error.message } + 7、错误代码为: ${ error.stack } + `); + return Promise.reject(error) + }) + } + + /** + * 添加请求拦截器 + * @constructor + */ + public async RequestInterceptor(): Promise { + this.instance.interceptors.request.use((config: InternalAxiosRequestConfig) => { + return config + }, function (error: AxiosError) { + return Promise.reject(error) + }) + } + +} + + +export default new AxiosInfra(); \ No newline at end of file diff --git a/src/infra/index.ts b/src/infra/index.ts new file mode 100644 index 0000000..829bef8 --- /dev/null +++ b/src/infra/index.ts @@ -0,0 +1 @@ +export {default as http} from "./AxiosInfra" \ No newline at end of file diff --git a/src/lib/README.md b/src/lib/README.md new file mode 100644 index 0000000..abfa02f --- /dev/null +++ b/src/lib/README.md @@ -0,0 +1 @@ +# 临时占位,上传空文件夹,保存文件结构用,后面删除 \ No newline at end of file diff --git a/src/lib/element-plus.ts b/src/lib/element-plus.ts new file mode 100644 index 0000000..1f515ac --- /dev/null +++ b/src/lib/element-plus.ts @@ -0,0 +1,22 @@ +// app-shared/src/lib/element-plus.ts +import ElementPlus from 'element-plus' +import 'element-plus/dist/index.css'; +import * as ElementPlusIcons from '@element-plus/icons-vue'; +import { App } from 'vue'; + + +export * from 'element-plus'; +export * as Icons from '@element-plus/icons-vue'; + +// 保留你的 init 函数 +export const init = (app: App) => { + app.use(ElementPlus); +}; + +// 如果你仍然需要一个默认导出,可以这样做 +const sharedUI = { + ...ElementPlus, // 合并 ElementPlus 的所有导出 + Icons: ElementPlusIcons, + init +}; +export default sharedUI; \ No newline at end of file diff --git a/src/main.ts b/src/main.ts new file mode 100644 index 0000000..1d727e0 --- /dev/null +++ b/src/main.ts @@ -0,0 +1,8 @@ +import { createApp } from 'vue' + +import App from './App' + + +createApp(App) + .mount('#app') + diff --git a/src/styles/common/index.ts b/src/styles/common/index.ts new file mode 100644 index 0000000..7bdd316 --- /dev/null +++ b/src/styles/common/index.ts @@ -0,0 +1,5 @@ +import "./normalize.less"; + +export const initNormalizeStyles = () => { + console.log("[app-shared] 清除默认样式normalize.less 已加载...") +} \ No newline at end of file diff --git a/src/styles/common/normalize.less b/src/styles/common/normalize.less new file mode 100644 index 0000000..377c5a3 --- /dev/null +++ b/src/styles/common/normalize.less @@ -0,0 +1,470 @@ +/* + * Copyright (c) 2020. bmy + */ + +html, body { + width: 100%; + height: 100%; +} + +body, h1, h2, h3, h4, h5, h6, p, a { + margin: 0; + padding: 0; + list-style: none; + text-decoration: none; +} + +html { + font-family: sans-serif; /* 1 */ + line-height: 1.15; /* 2 */ + -ms-text-size-adjust: 100%; /* 3 */ + -webkit-text-size-adjust: 100%; /* 3 */ +} + +body { + line-height: 1.5; + font-size: 16px; + width: 100%; + -webkit-tap-highlight-color: rgba(0, 0, 0, 0); + background-color: #fafafa; + color: rgba(0, 0, 0, 0.87); +} + +li { + list-style: none; +} + +article, +aside, +footer, +header, +nav, +section { + display: block; +} + +/** + * Correct the font size and margin on `h1` elements within `section` and + * `article` contexts in Chrome, Firefox, and Safari. + */ + +h1 { + font-size: 2em; + margin: 0.67em 0; +} + +/* Grouping content + ========================================================================== */ + +/** + * Add the correct display in IE 9-. + * 1. Add the correct display in IE. + */ + +figcaption, +figure, +main { /* 1 */ + display: block; +} + +/** + * Add the correct margin in IE 8. + */ + +figure { + margin: 1em 40px; +} + +/** + * 1. Add the correct box sizing in Firefox. + * 2. Show the overflow in Edge and IE. + */ + +hr { + box-sizing: content-box; /* 1 */ + height: 0; /* 1 */ + overflow: visible; /* 2 */ +} + +/** + * 1. Correct the inheritance and scaling of font size in all browsers. + * 2. Correct the odd `em` font sizing in all browsers. + */ + +pre { + font-family: monospace, monospace; /* 1 */ + font-size: 1em; /* 2 */ + white-space: pre-wrap; + word-break: break-all; + margin: 0; +} + +/* Text-level semantics + ========================================================================== */ + +/** + * 1. Remove the gray background on active links in IE 10. + * 2. Remove gaps in links underline in iOS 8+ and Safari 8+. + */ + +a { + text-decoration: none; + background-color: transparent; /* 1 */ + -webkit-text-decoration-skip: objects; /* 2 */ + user-select: none; + -webkit-user-select: none; +} + +/** + * Remove the outline on focused links when they are also active or hovered + * in all browsers (opinionated). + */ + +a:active, +a:hover { + outline-width: 0; +} + +/** + * 1. Remove the bottom border in Firefox 39-. + * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari. + */ + +abbr[title] { + border-bottom: none; /* 1 */ + text-decoration: underline; /* 2 */ + text-decoration: underline dotted; /* 2 */ +} + +/** + * Prevent the duplicate application of `bolder` by the next rule in Safari 6. + */ + +b, +strong { + font-weight: inherit; +} + +/** + * Add the correct font weight in Chrome, Edge, and Safari. + */ + +b, +strong { + font-weight: bolder; +} + +/** + * 1. Correct the inheritance and scaling of font size in all browsers. + * 2. Correct the odd `em` font sizing in all browsers. + */ + +code, +kbd, +samp { + font-family: monospace, monospace; /* 1 */ + font-size: 1em; /* 2 */ +} + +/** + * Add the correct font style in Android 4.3-. + */ + +dfn { + font-style: italic; +} + +/** + * Add the correct background and color in IE 9-. + */ + +mark { + background-color: #ff0; + color: #000; +} + +/** + * Add the correct font size in all browsers. + */ + +small { + font-size: 80%; +} + +/** + * Prevent `sub` and `sup` elements from affecting the line height in + * all browsers. + */ + +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} + +sub { + bottom: -0.25em; +} + +sup { + top: -0.5em; +} + +/* Embedded content + ========================================================================== */ + +/** + * Add the correct display in IE 9-. + */ + +audio, +video { + display: inline-block; +} + +/** + * Add the correct display in iOS 4-7. + */ + +audio:not([controls]) { + display: none; + height: 0; +} + +/** + * Remove the border on images inside links in IE 10-. + */ + +img { + border-style: none; +} + +/** + * Hide the overflow in IE. + */ + +svg:not(:root) { + overflow: hidden; +} + +/* Forms + ========================================================================== */ + +/** + * 1. Change the font styles in all browsers (opinionated). + * 2. Remove the margin in Firefox and Safari. + */ + +button, +input, +optgroup, +select, +textarea { + font-family: sans-serif; /* 1 */ + font-size: 100%; /* 1 */ + line-height: 1.15; /* 1 */ + margin: 0; /* 2 */ +} + +/** + * Show the overflow in IE. + * 1. Show the overflow in Edge. + */ + +button, +input { /* 1 */ + overflow: visible; +} + +/** + * Remove the inheritance of text transform in Edge, Firefox, and IE. + * 1. Remove the inheritance of text transform in Firefox. + */ + +button, +select { /* 1 */ + text-transform: none; +} + +/** + * 1. Prevent a WebKit bug where (2) destroys native `audio` and `video` + * controls in Android 4. + * 2. Correct the inability to style clickable types in iOS and Safari. + */ + +button, +html [type="button"], /* 1 */ +[type="reset"], +[type="submit"] { + -webkit-appearance: button; /* 2 */ +} + +/** + * Remove the inner border and padding in Firefox. + */ + +button::-moz-focus-inner, +[type="button"]::-moz-focus-inner, +[type="reset"]::-moz-focus-inner, +[type="submit"]::-moz-focus-inner { + border-style: none; + padding: 0; +} + +/** + * Restore the focus styles unset by the previous rule. + */ + +button:-moz-focusring, +[type="button"]:-moz-focusring, +[type="reset"]:-moz-focusring, +[type="submit"]:-moz-focusring { + outline: 1px dotted ButtonText; +} + +/** + * Change the border, margin, and padding in all browsers (opinionated). + */ + +fieldset { + border: 1px solid #c0c0c0; + margin: 0 2px; + padding: 0.35em 0.625em 0.75em; +} + +/** + * 1. Correct the text wrapping in Edge and IE. + * 2. Correct the color inheritance from `fieldset` elements in IE. + * 3. Remove the padding so developers are not caught out when they zero out + * `fieldset` elements in all browsers. + */ + +legend { + box-sizing: border-box; /* 1 */ + color: inherit; /* 2 */ + display: table; /* 1 */ + max-width: 100%; /* 1 */ + padding: 0; /* 3 */ + white-space: normal; /* 1 */ +} + +/** + * 1. Add the correct display in IE 9-. + * 2. Add the correct vertical alignment in Chrome, Firefox, and Opera. + */ + +progress { + display: inline-block; /* 1 */ + vertical-align: baseline; /* 2 */ +} + +/** + * Remove the default vertical scrollbar in IE. + */ + +textarea { + overflow: auto; + resize: vertical; +} + +/** + * 1. Add the correct box sizing in IE 10-. + * 2. Remove the padding in IE 10-. + */ + +[type="checkbox"], +[type="radio"] { + box-sizing: border-box; /* 1 */ + padding: 0; /* 2 */ +} + +/** + * Correct the cursor style of increment and decrement buttons in Chrome. + */ + +[type="number"]::-webkit-inner-spin-button, +[type="number"]::-webkit-outer-spin-button { + height: auto; +} + +/** + * 1. Correct the odd appearance in Chrome and Safari. + * 2. Correct the outline style in Safari. + */ + +[type="search"] { + -webkit-appearance: textfield; /* 1 */ + outline-offset: -2px; /* 2 */ +} + +/** + * Remove the inner padding and cancel buttons in Chrome and Safari on macOS. + */ + +[type="search"]::-webkit-search-cancel-button, +[type="search"]::-webkit-search-decoration { + -webkit-appearance: none; +} + +/** + * 1. Correct the inability to style clickable types in iOS and Safari. + * 2. Change font properties to `inherit` in Safari. + */ + +::-webkit-file-upload-button { + -webkit-appearance: button; /* 1 */ + font: inherit; /* 2 */ +} + +/* Interactive + ========================================================================== */ + +/* + * Add the correct display in IE 9-. + * 1. Add the correct display in Edge, IE, and Firefox. + */ + +details, /* 1 */ +menu { + display: block; +} + +/* + * Add the correct display in all browsers. + */ + +summary { + display: list-item; +} + +/* Scripting + ========================================================================== */ + +/** + * Add the correct display in IE 9-. + */ + +canvas { + display: inline-block; +} + +/** + * Add the correct display in IE. + */ + +template { + display: none; +} + +/* Hidden + ========================================================================== */ + +/** + * Add the correct display in IE 10-. + */ + +[hidden] { + display: none; +} diff --git a/src/styles/pub/index.ts b/src/styles/pub/index.ts new file mode 100644 index 0000000..4d76e67 --- /dev/null +++ b/src/styles/pub/index.ts @@ -0,0 +1,6 @@ +import "./variables.less"; +// import "./mixins.less"; + +export const initPubStyles = () => { + console.log("[app-shared] variables.less 和 mixins.less 已加载...") +} \ No newline at end of file diff --git a/src/styles/pub/mixins.less b/src/styles/pub/mixins.less new file mode 100644 index 0000000..9dc401f --- /dev/null +++ b/src/styles/pub/mixins.less @@ -0,0 +1,297 @@ +/* + * Copyright (c) 2020. bmy + */ + +.clearfix() { + &:after, + &:before { + content: " "; + display: table; + } + &:after { + clear: both; + } +} + +.hairline(@position, @color) when (@position = top) { + &:before { + content: ''; + position: absolute; + left: 0; + top: 0; + bottom: auto; + right: auto; + height: 1px; + width: 100%; + background-color: @color; + display: block; + z-index: 15; + // .transform-origin(50% 0%); + html.pixel-ratio-2 & { + .transform(scaleY(0.5)); + } + + html.pixel-ratio-3 & { + .transform(scaleY(0.33)); + } + } +} + +.hairline(@position, @color) when (@position = left) { + &:before { + content: ''; + position: absolute; + left: 0; + top: 0; + bottom: auto; + right: auto; + width: 1px; + height: 100%; + background-color: @color; + display: block; + z-index: 15; + // .transform-origin(0% 50%); + html.pixel-ratio-2 & { + .transform(scaleX(0.5)); + } + + html.pixel-ratio-3 & { + .transform(scaleX(0.33)); + } + } + +} + +.hairline(@position, @color) when (@position = bottom) { + &:after { + content: ''; + position: absolute; + left: 0; + bottom: 0; + right: auto; + top: auto; + height: 1px; + width: 100%; + background-color: @color; + display: block; + z-index: 15; + + html.pixel-ratio-2 & { + .transform(scaleY(0.5)); + } + + html.pixel-ratio-3 & { + .transform(scaleY(0.33)); + } + } +} + +.hairline(@position, @color) when (@position = right) { + &:after { + content: ''; + position: absolute; + right: 0; + top: 0; + left: auto; + bottom: auto; + width: 1px; + height: 100%; + background-color: @color; + display: block; + z-index: 15; + // .transform-origin(100% 50%); + html.pixel-ratio-2 & { + .transform(scaleX(0.5)); + } + + html.pixel-ratio-3 & { + .transform(scaleX(0.33)); + } + } +} + +// For right and bottom +.hairline-remove(@position) when not (@position = left) and not (@position = top) { + &:after { + display: none; + } +} + +// For left and top +.hairline-remove(@position) when not (@position = right) and not (@position = bottom) { + &:before { + display: none; + } +} + +// For right and bottom +.hairline-color(@position, @color) when not (@position = left) and not (@position = top) { + &:after { + background-color: @color; + } +} + +// For left and top +.hairline-color(@position, @color) when not (@position = right) and not (@position = bottom) { + &:before { + background-color: @color; + } +} + +// Encoded SVG Background +.encoded-svg-background(@svg) { + @url: `encodeURIComponent(@{svg})`; + background-image: url("data:image/svg+xml;charset=utf-8,@{url}"); +} + +// Preserve3D +.preserve3d() { + -webkit-transform-style: preserve-3d; + -moz-transform-style: preserve-3d; + -ms-transform-style: preserve-3d; + transform-style: preserve-3d; +} + +// Shadow +.depth(@level:1) { + & when (@level = 0) { + box-shadow: none; + } + & when (@level = 1) { + box-shadow: 0 2px 1px -1px rgba(0, 0, 0, .2), 0 1px 1px 0 rgba(0, 0, 0, .14), 0 1px 3px 0 rgba(0, 0, 0, .12); + } + & when (@level = 2) { + box-shadow: 0 3px 1px -2px rgba(0, 0, 0, .2), 0 2px 2px 0 rgba(0, 0, 0, .14), 0 1px 5px 0 rgba(0, 0, 0, .12); + } + & when (@level = 3) { + box-shadow: 0 3px 3px -2px rgba(0, 0, 0, .2), 0 3px 4px 0 rgba(0, 0, 0, .14), 0 1px 8px 0 rgba(0, 0, 0, .12); + } + & when (@level = 4) { + box-shadow: 0 2px 4px -1px rgba(0, 0, 0, .2), 0 4px 5px 0 rgba(0, 0, 0, .14), 0 1px 10px 0 rgba(0, 0, 0, .12); + } + & when (@level = 5) { + box-shadow: 0 3px 5px -1px rgba(0, 0, 0, .2), 0 5px 8px 0 rgba(0, 0, 0, .14), 0 1px 14px 0 rgba(0, 0, 0, .12); + } + & when (@level = 6) { + box-shadow: 0 3px 5px -1px rgba(0, 0, 0, .2), 0 6px 10px 0 rgba(0, 0, 0, .14), 0 1px 18px 0 rgba(0, 0, 0, .12); + } + & when (@level = 7) { + box-shadow: 0 4px 5px -2px rgba(0, 0, 0, .2), 0 7px 10px 1px rgba(0, 0, 0, .14), 0 2px 16px 1px rgba(0, 0, 0, .12); + } + & when (@level = 8) { + box-shadow: 0 5px 5px -3px rgba(0, 0, 0, .2), 0 8px 10px 1px rgba(0, 0, 0, .14), 0 3px 14px 2px rgba(0, 0, 0, .12); + } + & when (@level = 9) { + box-shadow: 0 5px 6px -3px rgba(0, 0, 0, .2), 0 9px 12px 1px rgba(0, 0, 0, .14), 0 3px 16px 2px rgba(0, 0, 0, .12); + } + & when (@level = 10) { + box-shadow: 0 6px 6px -3px rgba(0, 0, 0, .2), 0 10px 14px 1px rgba(0, 0, 0, .14), 0 4px 18px 3px rgba(0, 0, 0, .12); + } + & when (@level = 11) { + box-shadow: 0 6px 7px -4px rgba(0, 0, 0, .2), 0 11px 15px 1px rgba(0, 0, 0, .14), 0 4px 20px 3px rgba(0, 0, 0, .12); + } + & when (@level = 12) { + box-shadow: 0 7px 8px -4px rgba(0, 0, 0, .2), 0 12px 17px 2px rgba(0, 0, 0, .14), 0 5px 22px 4px rgba(0, 0, 0, .12); + } + & when (@level = 13) { + box-shadow: 0 7px 8px -4px rgba(0, 0, 0, .2), 0 13px 19px 2px rgba(0, 0, 0, .14), 0 5px 24px 4px rgba(0, 0, 0, .12); + } + & when (@level = 14) { + box-shadow: 0 7px 9px -4px rgba(0, 0, 0, .2), 0 14px 21px 2px rgba(0, 0, 0, .14), 0 5px 26px 4px rgba(0, 0, 0, .12); + } + & when (@level = 15) { + box-shadow: 0 8px 9px -5px rgba(0, 0, 0, .2), 0 15px 22px 2px rgba(0, 0, 0, .14), 0 6px 28px 5px rgba(0, 0, 0, .12); + } + & when (@level = 16) { + box-shadow: 0 8px 10px -5px rgba(0, 0, 0, .2), 0 16px 24px 2px rgba(0, 0, 0, .14), 0 6px 30px 5px rgba(0, 0, 0, .12); + } + & when (@level = 17) { + box-shadow: 0 8px 11px -5px rgba(0, 0, 0, .2), 0 17px 26px 2px rgba(0, 0, 0, .14), 0 6px 32px 5px rgba(0, 0, 0, .12); + } + & when (@level = 18) { + box-shadow: 0 9px 11px -5px rgba(0, 0, 0, .2), 0 18px 28px 2px rgba(0, 0, 0, .14), 0 7px 34px 6px rgba(0, 0, 0, .12); + } + & when (@level = 19) { + box-shadow: 0 9px 12px -6px rgba(0, 0, 0, .2), 0 19px 29px 2px rgba(0, 0, 0, .14), 0 7px 36px 6px rgba(0, 0, 0, .12); + } + & when (@level = 20) { + box-shadow: 0 10px 13px -6px rgba(0, 0, 0, .2), 0 20px 31px 3px rgba(0, 0, 0, .14), 0 8px 38px 7px rgba(0, 0, 0, .12); + } + & when (@level = 21) { + box-shadow: 0 10px 13px -6px rgba(0, 0, 0, .2), 0 21px 33px 3px rgba(0, 0, 0, .14), 0 8px 40px 7px rgba(0, 0, 0, .12); + } + & when (@level = 22) { + box-shadow: 0 10px 14px -6px rgba(0, 0, 0, .2), 0 22px 35px 3px rgba(0, 0, 0, .14), 0 8px 42px 7px rgba(0, 0, 0, .12); + } + & when (@level = 23) { + box-shadow: 0 11px 14px -7px rgba(0, 0, 0, .2), 0 23px 36px 3px rgba(0, 0, 0, .14), 0 9px 44px 8px rgba(0, 0, 0, .12); + } + & when (@level = 24) { + box-shadow: 0 11px 15px -7px rgba(0, 0, 0, .2), 0 24px 38px 3px rgba(0, 0, 0, .14), 0 9px 46px 8px rgba(0, 0, 0, .12); + } + // & when (@level = 1) { + // box-shadow: rgba(0, 0, 0, 0.117647) 0px 1px 6px, rgba(0, 0, 0, 0.117647) 0px 1px 4px; + // } + // & when (@level = 2) { + // box-shadow: rgba(0, 0, 0, 0.156863) 0px 3px 10px, rgba(0, 0, 0, 0.227451) 0px 3px 10px; + // } + // & when (@level = 3) { + // box-shadow: rgba(0, 0, 0, 0.188235) 0px 10px 30px, rgba(0, 0, 0, 0.227451) 0px 6px 10px; + // } + // & when (@level = 4) { + // box-shadow: rgba(0, 0, 0, 0.247059) 0px 14px 45px, rgba(0, 0, 0, 0.219608) 0px 10px 18px; + // } + // & when (@level = 5) { + // box-shadow: rgba(0, 0, 0, 0.298039) 0px 19px 60px, rgba(0, 0, 0, 0.219608) 0px 15px 20px; + // } +} + +// Highlighted Links +.active-highlight(@color:rgba(255, 255, 255, 0.15)) { + &:before { + content: ''; + width: 100%; + height: 100%; + position: absolute; + left: 0; + top: 0; + background-color: @color; + background-repeat: no-repeat; + background-position: center; + background-size: 100% 100%; + opacity: 0; + pointer-events: none; + .transition(600ms); + } + &.active-state:before, + html:not(.watch-active-state) &:active:before { + opacity: 1; + .transition(150ms); + } +} + +.active-highlight-color(@color) { + &:before { + background-image: -webkit-radial-gradient(center, circle cover, @color 66%, rgba(red(@color), green(@color), blue(@color), 0) 66%); + background-image: radial-gradient(circle at center, @color 66%, rgba(red(@color), green(@color), blue(@color), 0) 66%); + } +} + +// No Scrollbar +.no-scrollbar() { + &::-webkit-scrollbar { + display: none !important; + width: 0 !important; + height: 0 !important; + -webkit-appearance: none; + opacity: 0 !important; + } +} + + +.ellipsis() { + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; + word-wrap: break-word; +} diff --git a/src/styles/pub/variables.less b/src/styles/pub/variables.less new file mode 100644 index 0000000..f29fcb3 --- /dev/null +++ b/src/styles/pub/variables.less @@ -0,0 +1,9 @@ +/* + * Copyright (c) 2020. bmy + */ + +// 公共css样式,可以在 页面组件中直接使用变量名称 +:root { + --theme-color: red; + --test-color: yellow; +} \ No newline at end of file diff --git a/src/types/env.d.ts b/src/types/env.d.ts new file mode 100644 index 0000000..11f02fe --- /dev/null +++ b/src/types/env.d.ts @@ -0,0 +1 @@ +/// diff --git a/src/types/global.d.ts b/src/types/global.d.ts new file mode 100644 index 0000000..4c6d3ea --- /dev/null +++ b/src/types/global.d.ts @@ -0,0 +1,13 @@ +export interface AppConfig { + VITE_DEVBASEURL: string; + VITE_PRODBASEURL: string; + VITE_HTTPTIMEOUT: string; +} + +declare global { + interface Window { + __APP_CONFIG__: AppConfig; + } +} + +export {}; \ No newline at end of file diff --git a/src/utils/UrlCheckUtils.ts b/src/utils/UrlCheckUtils.ts new file mode 100644 index 0000000..e918832 --- /dev/null +++ b/src/utils/UrlCheckUtils.ts @@ -0,0 +1,12 @@ +import { HttpConfig } from '../config' + +export class UrlCheckUtils { + + public static check() { + if (import.meta.env.MODE === 'production') { + return HttpConfig.ProdBaseUrl; + } else { + return HttpConfig.DevBaseUrl; + } + } +} \ No newline at end of file diff --git a/src/utils/index.ts b/src/utils/index.ts new file mode 100644 index 0000000..4c6b9cc --- /dev/null +++ b/src/utils/index.ts @@ -0,0 +1 @@ +export * from './UrlCheckUtils'; \ No newline at end of file diff --git a/swarm.yml b/swarm.yml new file mode 100644 index 0000000..11c1acd --- /dev/null +++ b/swarm.yml @@ -0,0 +1,39 @@ +version: "3.8" # Docker Compose 文件版本,3.8 支持 Swarm 模式的所有功能 + +services: + app-shared: + image: registry.bmycode.help/app-shared:{{IMAGE_TAG}} + ports: + - target: 80 # 容器内部端口 + published: 1301 # 宿主机对外暴露端口 + protocol: tcp # 协议类型 + mode: ingress # 模式:使用路由网格 + deploy: + replicas: 3 # 副本数量,在集群中运行 3 个容器实例 + placement: + max_replicas_per_node: 1 # 每个 Swarm 节点最多运行 1 个副本,确保高可用性 + restart_policy: + condition: any # 重启条件:任何情况下容器退出都重启 + delay: 5s # 重启延迟 5 秒 + max_attempts: 3 # 最大重启尝试次数 + update_config: # 滚动更新配置 + parallelism: 1 # 并行更新数量:一次只更新 1 个容器 + delay: 10s # 更新间隔:每个容器更新后等待 10 秒再更新下一个 + failure_action: rollback # 更新失败时自动回滚到上一版本 + order: start-first # 更新顺序:先启动新容器,确认健康后再停止旧容器 + rollback_config: # 回滚配置 + parallelism: 1 # 回滚时并行数量 + delay: 5s # 回滚延迟 + resources: # 资源限制 + limits: # 资源上限 + cpus: "0.50" # 最多使用 0.5 个 CPU 核心 + memory: "256M" # 最多使用 256MB 内存 + reservations: # 资源预留 + cpus: "0.25" # 预留 0.25 个 CPU 核心 + memory: "128M" # 预留 128MB 内存 + networks: + - frontend # 连接到 frontend 网络 + +networks: + frontend: # 定义 frontend 网络 + driver: overlay # 使用 overlay 网络驱动,支持跨主机通信 \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..81f8cfc --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,45 @@ +{ + "include": [ + "src/**/*.ts", + "src/**/*.d.ts", + "src/**/*" + ], + "exclude": [ + "node_modules", + "dist", + "**/*.test.ts" + ], + "compilerOptions": { + "target": "ES2020", + "lib": [ + "ES2020", + "DOM", + "DOM.Iterable" + ], + "jsx": "preserve", + "jsxImportSource": "vue", + "module": "ESNext", + "moduleResolution": "bundler", + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "verbatimModuleSyntax": false, + "preserveValueImports": false, + "resolveJsonModule": true, + "isolatedModules": true, + "skipLibCheck": true, + "paths": { + "@/*": [ + "./src/app/*" + ], + "@shared/*": [ + "./src/shared/*" + ], + "@features/*": [ + "./src/features/*" + ], + "@domains/*": [ + "./src/domains/*" + ] + } + } +} \ No newline at end of file diff --git a/vite.config.ts b/vite.config.ts new file mode 100644 index 0000000..ee48271 --- /dev/null +++ b/vite.config.ts @@ -0,0 +1,57 @@ +import { defineConfig, loadEnv, UserConfig } from 'vite' +import federation from "vite-federation"; +// @ts-ignore +import vueJsx from '@vitejs/plugin-vue-jsx' + +// @ts-ignore +import vue from '@vitejs/plugin-vue'; +import visualizer from 'rollup-plugin-visualizer'; +import { reg, init } from 'vite-plugin-nacos'; + + +export default defineConfig(async ({ mode }): Promise => { + const env = loadEnv(mode, process.cwd()); + init(env) + return { + plugins: [ + reg(), + federation({ + name: env.VITE_SERVER_NAME, + transformFileTypes: ['.css', '.less'], + exposes: { + "./config": "./src/config/index.ts", + "./infra": "./src/infra/index.ts", + "./utils": "./src/utils/index.ts", + "./element-plus": "./src/lib/element-plus.ts", + "./normalize": "./src/styles/common/index.ts", + "./pub": "./src/styles/pub/index.ts", + }, + shared: { + vue: { import: false, generate: false, requiredVersion: '^3.5.18' }, + axios: { requiredVersion: '^1.12.2' }, + 'element-plus': { requiredVersion: '^2.11.3' } + } + }), + vue(), + vueJsx(), + visualizer({ + title: "共享模块依赖分析", + filename: 'dist/stats.html', // 分析文件输出路径 + open: false, // 构建完成后自动打开 + gzipSize: true, // 显示gzip后的大小 + brotliSize: true, // 显示brotli后的大小 + }) + ], + build: { + target: 'esnext', + minify: false, + cssCodeSplit: true, + rollupOptions: { + output: { + format: 'esm', + minifyInternalExports: false + } + } + } + } +}); \ No newline at end of file