├── .npmrc
├── src
├── views
│ ├── 404
│ │ └── index.vue
│ ├── articles
│ │ ├── index.vue
│ │ └── list
│ │ │ ├── components
│ │ │ └── form
│ │ │ │ ├── index.vue
│ │ │ │ └── script.js
│ │ │ ├── script.js
│ │ │ └── index.vue
│ ├── researches
│ │ ├── index.vue
│ │ └── list
│ │ │ ├── components
│ │ │ └── form
│ │ │ │ ├── index.vue
│ │ │ │ └── script.js
│ │ │ ├── script.js
│ │ │ └── index.vue
│ ├── team-members
│ │ ├── index.vue
│ │ └── list
│ │ │ ├── script.js
│ │ │ ├── components
│ │ │ └── form
│ │ │ │ ├── script.js
│ │ │ │ └── index.vue
│ │ │ └── index.vue
│ ├── home
│ │ └── index.vue
│ ├── login
│ │ ├── style.scss
│ │ ├── components
│ │ │ └── form
│ │ │ │ ├── style.scss
│ │ │ │ ├── script.js
│ │ │ │ └── index.vue
│ │ ├── script.js
│ │ └── index.vue
│ ├── orders
│ │ └── list
│ │ │ ├── components
│ │ │ └── detail
│ │ │ │ ├── style.scss
│ │ │ │ ├── script.js
│ │ │ │ └── index.vue
│ │ │ ├── script.js
│ │ │ └── index.vue
│ ├── users
│ │ └── list
│ │ │ ├── script.js
│ │ │ └── index.vue
│ ├── contents
│ │ ├── index.vue
│ │ └── script.js
│ ├── ads
│ │ └── list
│ │ │ ├── script.js
│ │ │ ├── components
│ │ │ └── form
│ │ │ │ ├── index.vue
│ │ │ │ └── script.js
│ │ │ └── index.vue
│ ├── jobs
│ │ └── list
│ │ │ ├── script.js
│ │ │ ├── components
│ │ │ └── form
│ │ │ │ ├── script.js
│ │ │ │ └── index.vue
│ │ │ └── index.vue
│ ├── categories
│ │ └── list
│ │ │ ├── components
│ │ │ └── form
│ │ │ │ ├── index.vue
│ │ │ │ └── script.js
│ │ │ ├── script.js
│ │ │ └── index.vue
│ ├── app-upgrades
│ │ └── list
│ │ │ ├── script.js
│ │ │ ├── components
│ │ │ └── form
│ │ │ │ ├── script.js
│ │ │ │ └── index.vue
│ │ │ └── index.vue
│ └── products
│ │ └── list
│ │ ├── script.js
│ │ ├── components
│ │ └── form
│ │ │ ├── script.js
│ │ │ └── index.vue
│ │ └── index.vue
├── static
│ └── logo.png
├── assets
│ └── styles
│ │ ├── global
│ │ ├── classes
│ │ │ └── index.scss
│ │ ├── components
│ │ │ ├── index.scss
│ │ │ ├── iconfont.scss
│ │ │ └── iconfont-20230706.scss
│ │ ├── utils
│ │ │ └── base.scss
│ │ ├── reset
│ │ │ └── index.scss
│ │ └── index.scss
│ │ ├── utils
│ │ ├── index.scss
│ │ └── variables.scss
│ │ └── element-plus
│ │ ├── variables.scss
│ │ └── index.scss
├── router
│ ├── routes
│ │ ├── private
│ │ │ ├── home.js
│ │ │ ├── contents.js
│ │ │ ├── ads.js
│ │ │ ├── articles.js
│ │ │ ├── jobs.js
│ │ │ ├── users.js
│ │ │ ├── orders.js
│ │ │ ├── researches.js
│ │ │ ├── products.js
│ │ │ ├── team-members.js
│ │ │ ├── categories.js
│ │ │ ├── app-upgrades.js
│ │ │ └── index.js
│ │ └── public
│ │ │ ├── index.js
│ │ │ ├── login.js
│ │ │ └── logout.js
│ └── index.js
├── apis
│ └── admin
│ │ ├── ads.js
│ │ ├── jobs.js
│ │ ├── files.js
│ │ ├── orders.js
│ │ ├── users.js
│ │ ├── articles.js
│ │ ├── contents.js
│ │ ├── products.js
│ │ ├── categories.js
│ │ ├── researches.js
│ │ ├── ali-cloud-oss.js
│ │ ├── app-upgrades.js
│ │ ├── team-members.js
│ │ └── tencent-cloud-cos.js
├── store
│ ├── modules
│ │ └── index.js
│ ├── index.js
│ ├── enums.js
│ └── user.js
├── composables
│ ├── use-helpers.js
│ └── use-consts.js
├── utils
│ ├── config.js
│ └── use-components.js
├── components
│ └── layout
│ │ ├── index.vue
│ │ ├── script.js
│ │ └── utils
│ │ └── get-menus.js
├── App.vue
└── main.js
├── .gitignore
├── npm
├── iview.zip
└── element-plus-admin
│ ├── utils
│ ├── polyfills.js
│ └── create-api.js
│ ├── assets
│ └── styles
│ │ ├── global
│ │ ├── reset
│ │ │ └── root.scss
│ │ ├── components
│ │ │ ├── container.scss
│ │ │ └── scrollbar.scss
│ │ └── utils
│ │ │ └── base.scss
│ │ ├── element-plus
│ │ ├── reset.scss
│ │ └── components.scss
│ │ └── classes
│ │ └── base-colors.scss
│ ├── components
│ ├── layout
│ │ ├── header
│ │ │ ├── script.js
│ │ │ ├── index.vue
│ │ │ └── style.scss
│ │ ├── main
│ │ │ ├── style.scss
│ │ │ ├── index.vue
│ │ │ └── script.js
│ │ └── sidebar
│ │ │ ├── style.scss
│ │ │ ├── index.vue
│ │ │ └── script.js
│ ├── login
│ │ ├── components
│ │ │ ├── footer
│ │ │ │ ├── index.vue
│ │ │ │ └── style.scss
│ │ │ ├── main
│ │ │ │ ├── script.js
│ │ │ │ ├── images
│ │ │ │ │ ├── bg.png
│ │ │ │ │ └── icon.png
│ │ │ │ ├── index.vue
│ │ │ │ └── style.scss
│ │ │ └── header
│ │ │ │ ├── index.vue
│ │ │ │ └── style.scss
│ │ ├── style.scss
│ │ └── index.vue
│ ├── order-input
│ │ ├── index.vue
│ │ └── script.js
│ ├── upload
│ │ ├── style.scss
│ │ ├── files
│ │ │ ├── index.vue
│ │ │ ├── style.scss
│ │ │ └── script.js
│ │ ├── index.vue
│ │ ├── script.js
│ │ └── composables
│ │ │ └── use-cos.js
│ ├── list
│ │ ├── components
│ │ │ └── image
│ │ │ │ ├── script.js
│ │ │ │ ├── style.scss
│ │ │ │ └── index.vue
│ │ ├── composables
│ │ │ ├── use-helpers.js
│ │ │ └── use-list.js
│ │ ├── style.scss
│ │ ├── index.vue
│ │ └── script.js
│ ├── file-viewer
│ │ ├── components
│ │ │ ├── office-viewer
│ │ │ │ ├── style.scss
│ │ │ │ ├── index.vue
│ │ │ │ └── script.js
│ │ │ └── video-viewer
│ │ │ │ ├── style.scss
│ │ │ │ ├── index.vue
│ │ │ │ └── script.js
│ │ ├── index.vue
│ │ └── script.js
│ ├── items
│ │ ├── index.vue
│ │ └── script.js
│ ├── enum-select
│ │ ├── index.vue
│ │ └── script.js
│ ├── editor
│ │ ├── index.vue
│ │ ├── composables
│ │ │ └── use-upload.js
│ │ └── script.js
│ ├── file-list
│ │ ├── index.vue
│ │ └── script.js
│ └── resource-select
│ │ ├── index.vue
│ │ └── script.js
│ ├── enums
│ └── upload-to.js
│ ├── apis
│ └── public
│ │ ├── enums.js
│ │ ├── files.js
│ │ └── managers.js
│ ├── composables
│ ├── use-xlsx.js
│ ├── use-enums.js
│ ├── use-auth.js
│ └── use-form-dialog.js
│ ├── store
│ └── modules
│ │ ├── enums.js
│ │ ├── items.js
│ │ └── auth.js
│ └── package.json
├── babel.config.js
├── public
├── favicon.ico
├── static
│ └── logo.png
└── index.html
├── .env.test
├── .env.development
├── .env.production
├── alias.config.js
├── vue.config.js
├── package.json
└── README.md
/.npmrc:
--------------------------------------------------------------------------------
1 | registry=https://registry.npmmirror.com
2 |
--------------------------------------------------------------------------------
/src/views/articles/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/src/views/researches/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .DS_Store
3 | dist
4 | dist-ssr
5 | *.local
6 | /.idea
7 |
--------------------------------------------------------------------------------
/npm/iview.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zhaotoday/element-plus/HEAD/npm/iview.zip
--------------------------------------------------------------------------------
/src/views/team-members/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: ["@vue/cli-plugin-babel/preset"],
3 | };
4 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zhaotoday/element-plus/HEAD/public/favicon.ico
--------------------------------------------------------------------------------
/src/static/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zhaotoday/element-plus/HEAD/src/static/logo.png
--------------------------------------------------------------------------------
/public/static/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zhaotoday/element-plus/HEAD/public/static/logo.png
--------------------------------------------------------------------------------
/src/views/home/index.vue:
--------------------------------------------------------------------------------
1 |
2 | 欢迎使用后台管理系统!
3 |
4 |
--------------------------------------------------------------------------------
/.env.test:
--------------------------------------------------------------------------------
1 | VUE_APP_API_URL=https://my-app-api.test.liruan.cn
2 | VUE_APP_STATIC_URL=https://my-app.test.lrcdn.cn
--------------------------------------------------------------------------------
/.env.development:
--------------------------------------------------------------------------------
1 | VUE_APP_API_URL=http://localhost:11010
2 | VUE_APP_STATIC_URL=http://localhost:11010/public/files
3 |
--------------------------------------------------------------------------------
/npm/element-plus-admin/utils/polyfills.js:
--------------------------------------------------------------------------------
1 | import "core-js/es/promise";
2 | import "core-js/es/array/includes";
3 |
--------------------------------------------------------------------------------
/src/assets/styles/global/classes/index.scss:
--------------------------------------------------------------------------------
1 | @import "node_modules/element-plus-admin/assets/styles/classes/base-colors";
2 |
--------------------------------------------------------------------------------
/src/views/404/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/src/assets/styles/utils/index.scss:
--------------------------------------------------------------------------------
1 | @import "~sass-utils";
2 | @import "~sass-utils/pc/utils/variables";
3 | @import "./variables";
4 |
--------------------------------------------------------------------------------
/.env.production:
--------------------------------------------------------------------------------
1 | VUE_APP_API_URL=https://xmxhsh-mall-api.liruan.cn
2 | VUE_APP_STATIC_URL=https://xmxhsh-mall-api.liruan.cn/public/files
3 |
--------------------------------------------------------------------------------
/src/views/login/style.scss:
--------------------------------------------------------------------------------
1 | @import "~@/assets/styles/utils";
2 |
3 | @include v(login) {
4 | @import "./components/form/style";
5 | }
6 |
--------------------------------------------------------------------------------
/src/router/routes/private/home.js:
--------------------------------------------------------------------------------
1 | export const homeRoute = {
2 | path: "/",
3 | component: () => import("@/views/home/index.vue"),
4 | };
5 |
--------------------------------------------------------------------------------
/npm/element-plus-admin/assets/styles/global/reset/root.scss:
--------------------------------------------------------------------------------
1 | html,
2 | body,
3 | #app {
4 | @include size(100%, 100%);
5 |
6 | overflow: hidden;
7 | }
8 |
--------------------------------------------------------------------------------
/npm/element-plus-admin/components/layout/header/script.js:
--------------------------------------------------------------------------------
1 | export default {
2 | props: {
3 | userName: String,
4 | },
5 | emits: ["logout"],
6 | };
7 |
--------------------------------------------------------------------------------
/npm/element-plus-admin/components/login/components/footer/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
--------------------------------------------------------------------------------
/npm/element-plus-admin/enums/upload-to.js:
--------------------------------------------------------------------------------
1 | export const UploadTo = {
2 | Server: "Server",
3 | AliCloudOss: "AliCloudOss",
4 | TencentCloudOss: "TencentCloudOss",
5 | };
6 |
--------------------------------------------------------------------------------
/src/router/routes/private/contents.js:
--------------------------------------------------------------------------------
1 | export const contentRoute = {
2 | path: ":menu/contents-:path",
3 | component: () => import("@/views/contents/index.vue"),
4 | };
5 |
--------------------------------------------------------------------------------
/src/router/routes/public/index.js:
--------------------------------------------------------------------------------
1 | import { loginRoute } from "./login";
2 | import { logoutRoute } from "./logout";
3 |
4 | export const publicRoutes = [loginRoute, logoutRoute];
5 |
--------------------------------------------------------------------------------
/npm/element-plus-admin/apis/public/enums.js:
--------------------------------------------------------------------------------
1 | import { createApi } from "../../utils/create-api";
2 |
3 | export const publicEnumsApi = createApi({
4 | url: "/public/dicts",
5 | });
6 |
--------------------------------------------------------------------------------
/npm/element-plus-admin/apis/public/files.js:
--------------------------------------------------------------------------------
1 | import { createApi } from "../../utils/create-api";
2 |
3 | export const publicFilesApi = createApi({
4 | url: "/public/files",
5 | });
6 |
--------------------------------------------------------------------------------
/npm/element-plus-admin/components/login/components/main/script.js:
--------------------------------------------------------------------------------
1 | export default {
2 | props: {
3 | tip: {
4 | type: String,
5 | default: "",
6 | },
7 | },
8 | };
9 |
--------------------------------------------------------------------------------
/npm/element-plus-admin/apis/public/managers.js:
--------------------------------------------------------------------------------
1 | import { createApi } from "../../utils/create-api";
2 |
3 | export const publicManagersApi = createApi({
4 | url: "/public/managers",
5 | });
6 |
--------------------------------------------------------------------------------
/npm/element-plus-admin/components/login/components/main/images/bg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zhaotoday/element-plus/HEAD/npm/element-plus-admin/components/login/components/main/images/bg.png
--------------------------------------------------------------------------------
/npm/element-plus-admin/components/login/components/main/images/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zhaotoday/element-plus/HEAD/npm/element-plus-admin/components/login/components/main/images/icon.png
--------------------------------------------------------------------------------
/npm/element-plus-admin/assets/styles/global/components/container.scss:
--------------------------------------------------------------------------------
1 | @include c(container) {
2 | @include clearfix;
3 | @include block--center;
4 |
5 | box-sizing: border-box;
6 | position: relative;
7 | width: 1240px;
8 | }
9 |
--------------------------------------------------------------------------------
/npm/element-plus-admin/components/login/components/footer/style.scss:
--------------------------------------------------------------------------------
1 | @import "~@/assets/styles/utils";
2 |
3 | @include b(footer) {
4 | @include position--fixed(null, null, 0, 0);
5 | @include text--middle(80px);
6 |
7 | width: 100%;
8 | }
9 |
--------------------------------------------------------------------------------
/npm/element-plus-admin/components/login/style.scss:
--------------------------------------------------------------------------------
1 | @import "~@/assets/styles/utils";
2 |
3 | @include c(login) {
4 | @import "./components/header/style";
5 | @import "./components/main/style";
6 | @import "./components/footer/style";
7 | }
8 |
--------------------------------------------------------------------------------
/src/assets/styles/element-plus/variables.scss:
--------------------------------------------------------------------------------
1 | @use "sass:map";
2 |
3 | @forward "node_modules/element-plus/theme-chalk/src/common/var.scss" with (
4 | $colors: (
5 | "primary": (
6 | "base": #0080ff,
7 | ),
8 | )
9 | );
10 |
--------------------------------------------------------------------------------
/src/assets/styles/global/components/index.scss:
--------------------------------------------------------------------------------
1 | @import "node_modules/element-plus-admin/assets/styles/global/components/container";
2 | @import "node_modules/element-plus-admin/assets/styles/global/components/scrollbar";
3 |
4 | @import "./iconfont";
5 |
--------------------------------------------------------------------------------
/npm/element-plus-admin/components/login/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/src/apis/admin/ads.js:
--------------------------------------------------------------------------------
1 | import { createApi } from "element-plus-admin/utils/create-api";
2 | import { useAuth } from "element-plus-admin/composables/use-auth";
3 |
4 | export const adsApi = createApi({
5 | url: "/admin/ads",
6 | headers: useAuth().getHeaders,
7 | });
8 |
--------------------------------------------------------------------------------
/src/apis/admin/jobs.js:
--------------------------------------------------------------------------------
1 | import { createApi } from "element-plus-admin/utils/create-api";
2 | import { useAuth } from "element-plus-admin/composables/use-auth";
3 |
4 | export const jobsApi = createApi({
5 | url: "/admin/jobs",
6 | headers: useAuth().getHeaders,
7 | });
8 |
--------------------------------------------------------------------------------
/src/store/modules/index.js:
--------------------------------------------------------------------------------
1 | import { auth } from "element-plus-admin/store/modules/auth";
2 | import { enums } from "element-plus-admin/store/modules/enums";
3 | import { items } from "element-plus-admin/store/modules/items";
4 |
5 | export default { auth, enums, items };
6 |
--------------------------------------------------------------------------------
/src/apis/admin/files.js:
--------------------------------------------------------------------------------
1 | import { createApi } from "element-plus-admin/utils/create-api";
2 | import { useAuth } from "element-plus-admin/composables/use-auth";
3 |
4 | export const filesApi = createApi({
5 | url: "/admin/files",
6 | headers: useAuth().getHeaders,
7 | });
8 |
--------------------------------------------------------------------------------
/src/apis/admin/orders.js:
--------------------------------------------------------------------------------
1 | import { createApi } from "element-plus-admin/utils/create-api";
2 | import { useAuth } from "element-plus-admin/composables/use-auth";
3 |
4 | export const ordersApi = createApi({
5 | url: "/admin/orders",
6 | headers: useAuth().getHeaders,
7 | });
8 |
--------------------------------------------------------------------------------
/src/apis/admin/users.js:
--------------------------------------------------------------------------------
1 | import { createApi } from "element-plus-admin/utils/create-api";
2 | import { useAuth } from "element-plus-admin/composables/use-auth";
3 |
4 | export const usersApi = createApi({
5 | url: "/admin/users",
6 | headers: useAuth().getHeaders,
7 | });
8 |
--------------------------------------------------------------------------------
/src/assets/styles/global/utils/base.scss:
--------------------------------------------------------------------------------
1 | @use "sass:map";
2 |
3 | @import "~@/assets/styles/utils";
4 |
5 | .u-shadow {
6 | box-shadow: 0 0 6px 2px rgba(168, 168, 171, 0.16);
7 |
8 | &:hover {
9 | box-shadow: 0 0 6px 2px rgba(168, 168, 171, 0.26);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/apis/admin/articles.js:
--------------------------------------------------------------------------------
1 | import { createApi } from "element-plus-admin/utils/create-api";
2 | import { useAuth } from "element-plus-admin/composables/use-auth";
3 |
4 | export const articlesApi = createApi({
5 | url: "/admin/articles",
6 | headers: useAuth().getHeaders,
7 | });
8 |
--------------------------------------------------------------------------------
/src/apis/admin/contents.js:
--------------------------------------------------------------------------------
1 | import { createApi } from "element-plus-admin/utils/create-api";
2 | import { useAuth } from "element-plus-admin/composables/use-auth";
3 |
4 | export const contentsApi = createApi({
5 | url: "/admin/contents",
6 | headers: useAuth().getHeaders,
7 | });
8 |
--------------------------------------------------------------------------------
/src/apis/admin/products.js:
--------------------------------------------------------------------------------
1 | import { createApi } from "element-plus-admin/utils/create-api";
2 | import { useAuth } from "element-plus-admin/composables/use-auth";
3 |
4 | export const productsApi = createApi({
5 | url: "/admin/products",
6 | headers: useAuth().getHeaders,
7 | });
8 |
--------------------------------------------------------------------------------
/npm/element-plus-admin/components/login/components/header/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
--------------------------------------------------------------------------------
/src/apis/admin/categories.js:
--------------------------------------------------------------------------------
1 | import { createApi } from "element-plus-admin/utils/create-api";
2 | import { useAuth } from "element-plus-admin/composables/use-auth";
3 |
4 | export const categoriesApi = createApi({
5 | url: "/admin/categories",
6 | headers: useAuth().getHeaders,
7 | });
8 |
--------------------------------------------------------------------------------
/src/apis/admin/researches.js:
--------------------------------------------------------------------------------
1 | import { createApi } from "element-plus-admin/utils/create-api";
2 | import { useAuth } from "element-plus-admin/composables/use-auth";
3 |
4 | export const researchesApi = createApi({
5 | url: "/admin/researches",
6 | headers: useAuth().getHeaders,
7 | });
8 |
--------------------------------------------------------------------------------
/src/assets/styles/element-plus/index.scss:
--------------------------------------------------------------------------------
1 | @use "sass:map";
2 |
3 | @import "node_modules/element-plus/theme-chalk/src";
4 |
5 | @import "node_modules/element-plus-admin/assets/styles/element-plus/components";
6 | @import "node_modules/element-plus-admin/assets/styles/element-plus/reset";
7 |
--------------------------------------------------------------------------------
/src/apis/admin/ali-cloud-oss.js:
--------------------------------------------------------------------------------
1 | import { createApi } from "element-plus-admin/utils/create-api";
2 | import { useAuth } from "element-plus-admin/composables/use-auth";
3 |
4 | export const aliCloudOssApi = createApi({
5 | url: "/admin/aliCloudOss",
6 | headers: useAuth().getHeaders,
7 | });
8 |
--------------------------------------------------------------------------------
/src/apis/admin/app-upgrades.js:
--------------------------------------------------------------------------------
1 | import { createApi } from "element-plus-admin/utils/create-api";
2 | import { useAuth } from "element-plus-admin/composables/use-auth";
3 |
4 | export const appUpgradesApi = createApi({
5 | url: "/admin/appUpgrades",
6 | headers: useAuth().getHeaders,
7 | });
8 |
--------------------------------------------------------------------------------
/src/apis/admin/team-members.js:
--------------------------------------------------------------------------------
1 | import { createApi } from "element-plus-admin/utils/create-api";
2 | import { useAuth } from "element-plus-admin/composables/use-auth";
3 |
4 | export const teamMembersApi = createApi({
5 | url: "/admin/teamMembers",
6 | headers: useAuth().getHeaders,
7 | });
8 |
--------------------------------------------------------------------------------
/npm/element-plus-admin/assets/styles/global/utils/base.scss:
--------------------------------------------------------------------------------
1 | @use "sass:map";
2 |
3 | @import "~@/assets/styles/utils";
4 |
5 | .u-shadow {
6 | box-shadow: 0 0 6px 2px rgba(168, 168, 171, 0.16);
7 |
8 | &:hover {
9 | box-shadow: 0 0 6px 2px rgba(168, 168, 171, 0.26);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/apis/admin/tencent-cloud-cos.js:
--------------------------------------------------------------------------------
1 | import { createApi } from "element-plus-admin/utils/create-api";
2 | import { useAuth } from "element-plus-admin/composables/use-auth";
3 |
4 | export const tencentCloudCosApi = createApi({
5 | url: "/admin/tencentCloudCos",
6 | headers: useAuth().getHeaders,
7 | });
8 |
--------------------------------------------------------------------------------
/npm/element-plus-admin/components/order-input/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/src/assets/styles/global/reset/index.scss:
--------------------------------------------------------------------------------
1 | @use "sass:map";
2 |
3 | @import "node_modules/sass-utils/pc/global/reset/base";
4 |
5 | @import "node_modules/element-plus-admin/assets/styles/global/reset/root";
6 |
7 | body {
8 | color: map.get($text-color, regular);
9 | background-color: #eff3f8;
10 | }
11 |
--------------------------------------------------------------------------------
/src/router/routes/private/ads.js:
--------------------------------------------------------------------------------
1 | import { RouterView } from "vue-router";
2 |
3 | export const adsRoute = {
4 | path: ":menu/ads",
5 | component: ,
6 | children: [
7 | {
8 | path: "",
9 | component: () => import("@/views/ads/list/index.vue"),
10 | },
11 | ],
12 | };
13 |
--------------------------------------------------------------------------------
/npm/element-plus-admin/components/upload/style.scss:
--------------------------------------------------------------------------------
1 | @import "~@/assets/styles/utils";
2 |
3 | @include c(upload) {
4 | @include e(tip) {
5 | @include text--middle(20px);
6 | }
7 |
8 | @include e(progress) {
9 | .el-progress__text {
10 | min-width: 20px !important;
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/router/routes/private/articles.js:
--------------------------------------------------------------------------------
1 | export const articlesRoute = {
2 | path: ":menu/articles-:path",
3 | component: () => import("@/views/articles/index.vue"),
4 | children: [
5 | {
6 | path: "",
7 | component: () => import("@/views/articles/list/index.vue"),
8 | },
9 | ],
10 | };
11 |
--------------------------------------------------------------------------------
/src/router/routes/private/jobs.js:
--------------------------------------------------------------------------------
1 | import { RouterView } from "vue-router";
2 |
3 | export const jobsRoute = {
4 | path: ":menu/jobs",
5 | component: ,
6 | children: [
7 | {
8 | path: "",
9 | component: () => import("@/views/jobs/list/index.vue"),
10 | },
11 | ],
12 | };
13 |
--------------------------------------------------------------------------------
/src/router/routes/private/users.js:
--------------------------------------------------------------------------------
1 | import { RouterView } from "vue-router";
2 |
3 | export const usersRoute = {
4 | path: ":menu/users",
5 | component: ,
6 | children: [
7 | {
8 | path: "",
9 | component: () => import("@/views/users/list/index.vue"),
10 | },
11 | ],
12 | };
13 |
--------------------------------------------------------------------------------
/src/router/routes/private/orders.js:
--------------------------------------------------------------------------------
1 | import { RouterView } from "vue-router";
2 |
3 | export const ordersRoute = {
4 | path: ":menu/orders",
5 | component: ,
6 | children: [
7 | {
8 | path: "",
9 | component: () => import("@/views/orders/list/index.vue"),
10 | },
11 | ],
12 | };
13 |
--------------------------------------------------------------------------------
/src/router/routes/private/researches.js:
--------------------------------------------------------------------------------
1 | export const researchesRoute = {
2 | path: ":menu/researches-:path",
3 | component: () => import("@/views/researches/index.vue"),
4 | children: [
5 | {
6 | path: "",
7 | component: () => import("@/views/researches/list/index.vue"),
8 | },
9 | ],
10 | };
11 |
--------------------------------------------------------------------------------
/npm/element-plus-admin/components/list/components/image/script.js:
--------------------------------------------------------------------------------
1 | export default {
2 | name: "ListImage",
3 | props: {
4 | src: String,
5 | width: {
6 | type: String,
7 | default: "100px",
8 | },
9 | height: {
10 | type: String,
11 | default: "100px",
12 | },
13 | },
14 | };
15 |
--------------------------------------------------------------------------------
/src/router/routes/private/products.js:
--------------------------------------------------------------------------------
1 | import { RouterView } from "vue-router";
2 |
3 | export const productsRoute = {
4 | path: ":menu/products",
5 | component: ,
6 | children: [
7 | {
8 | path: "",
9 | component: () => import("@/views/products/list/index.vue"),
10 | },
11 | ],
12 | };
13 |
--------------------------------------------------------------------------------
/src/router/routes/private/team-members.js:
--------------------------------------------------------------------------------
1 | export const teamMembersRoute = {
2 | path: ":menu/team-members-:path",
3 | component: () => import("@/views/team-members/index.vue"),
4 | children: [
5 | {
6 | path: "",
7 | component: () => import("@/views/team-members/list/index.vue"),
8 | },
9 | ],
10 | };
11 |
--------------------------------------------------------------------------------
/npm/element-plus-admin/components/list/components/image/style.scss:
--------------------------------------------------------------------------------
1 | @use "sass:map";
2 |
3 | @import "~@/assets/styles/utils";
4 |
5 | @include b(image) {
6 | border-radius: 4px;
7 | box-shadow: 0 0 6px 2px rgba(168, 168, 171, 0.16);
8 |
9 | &:hover {
10 | box-shadow: 0 0 6px 2px rgba(168, 168, 171, 0.26);
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/router/routes/private/categories.js:
--------------------------------------------------------------------------------
1 | import { RouterView } from "vue-router";
2 |
3 | export const categoriesRoute = {
4 | path: ":menu/categories",
5 | component: ,
6 | children: [
7 | {
8 | path: "",
9 | component: () => import("@/views/categories/list/index.vue"),
10 | },
11 | ],
12 | };
13 |
--------------------------------------------------------------------------------
/src/router/routes/private/app-upgrades.js:
--------------------------------------------------------------------------------
1 | import { RouterView } from "vue-router";
2 |
3 | export const appUpgradesRoute = {
4 | path: ":menu/app-upgrades",
5 | component: ,
6 | children: [
7 | {
8 | path: "",
9 | component: () => import("@/views/app-upgrades/list/index.vue"),
10 | },
11 | ],
12 | };
13 |
--------------------------------------------------------------------------------
/src/router/routes/public/login.js:
--------------------------------------------------------------------------------
1 | import { store } from "@/store";
2 |
3 | export const loginRoute = {
4 | path: "/login",
5 | component: () => import("@/views/login/index.vue"),
6 | beforeEnter(to, from, next) {
7 | if (store.state.auth.user.token) {
8 | next("/");
9 | } else {
10 | next();
11 | }
12 | },
13 | };
14 |
--------------------------------------------------------------------------------
/alias.config.js:
--------------------------------------------------------------------------------
1 | // WebStorm IDE:File -> Settings -> Languages & Frameworks -> JavaScript -> Webpack
2 | // -> webpack configuration file 选择 alias.config.js
3 |
4 | const resolve = (dir) => require("path").join(__dirname, dir);
5 |
6 | module.exports = {
7 | resolve: {
8 | alias: {
9 | "@": resolve("./src"),
10 | },
11 | },
12 | };
13 |
--------------------------------------------------------------------------------
/npm/element-plus-admin/components/file-viewer/components/office-viewer/style.scss:
--------------------------------------------------------------------------------
1 | @use "sass:map";
2 |
3 | @import "~@/assets/styles/utils";
4 |
5 | @include c(office-viewer) {
6 | .el-dialog__body {
7 | padding: 0 !important;
8 | }
9 |
10 | @include e(iframe) {
11 | @include size(100%, 700px);
12 |
13 | border: 0;
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/npm/element-plus-admin/components/file-viewer/components/video-viewer/style.scss:
--------------------------------------------------------------------------------
1 | @use "sass:map";
2 |
3 | @import "~@/assets/styles/utils";
4 |
5 | @include c(video-viewer) {
6 | .el-dialog__body {
7 | padding: 0 !important;
8 | }
9 |
10 | @include e(video) {
11 | @include size(100%, 540px);
12 |
13 | border: 0;
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/composables/use-helpers.js:
--------------------------------------------------------------------------------
1 | import helpers from "jt-helpers";
2 | import { useConsts } from "@/composables/use-consts";
3 |
4 | export const useHelpers = () => {
5 | const { ApiUrl } = useConsts();
6 |
7 | return {
8 | ...helpers,
9 | getFileUrl({ id }) {
10 | return `${ApiUrl}/public/files/${id}`;
11 | },
12 | };
13 | };
14 |
--------------------------------------------------------------------------------
/vue.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | // publicPath: "/fjqshadmin/",
3 | transpileDependencies: ["element-plus-admin"],
4 | configureWebpack: {
5 | module: {
6 | rules: [
7 | {
8 | test: /\.mjs$/,
9 | include: /node_modules/,
10 | type: "javascript/auto",
11 | },
12 | ],
13 | },
14 | },
15 | };
16 |
--------------------------------------------------------------------------------
/src/utils/config.js:
--------------------------------------------------------------------------------
1 | import time from "jt-time";
2 | import { useConsts } from "@/composables/use-consts";
3 | import { useHelpers } from "@/composables/use-helpers";
4 |
5 | export const config = (app) => {
6 | app.config.globalProperties.$time = time;
7 | app.config.globalProperties.$consts = useConsts();
8 | app.config.globalProperties.$helpers = useHelpers();
9 | };
10 |
--------------------------------------------------------------------------------
/npm/element-plus-admin/assets/styles/element-plus/reset.scss:
--------------------------------------------------------------------------------
1 | .el-dialog {
2 | &__body {
3 | padding: 20px !important;
4 | padding-bottom: 10px !important;
5 | }
6 | }
7 |
8 | .el-form-item {
9 | &__content {
10 | display: block !important;
11 | }
12 | }
13 |
14 | .el-form--inline {
15 | .el-form-item {
16 | margin-right: 10px !important;
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/npm/element-plus-admin/components/list/components/image/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/src/assets/styles/global/index.scss:
--------------------------------------------------------------------------------
1 | @import "~@/assets/styles/utils";
2 |
3 | @import "~sass-utils/global/reset/base";
4 | @import "~sass-utils/global/utils/base";
5 | @import "~sass-utils/pc/global";
6 | @import "~sass-utils/ui/element-plus/global";
7 |
8 | @import "./classes";
9 | @import "./components";
10 | @import "../element-plus";
11 | @import "./reset";
12 | @import "./utils/base";
13 |
--------------------------------------------------------------------------------
/src/router/routes/public/logout.js:
--------------------------------------------------------------------------------
1 | import { store } from "@/store";
2 | import { RouterView } from "vue-router";
3 |
4 | export const logoutRoute = {
5 | path: "/logout",
6 | component: ,
7 | async beforeEnter(to, from, next) {
8 | if (store.state.auth.user.token) {
9 | await store.dispatch("auth/logout");
10 | }
11 | next("/login");
12 | },
13 | };
14 |
--------------------------------------------------------------------------------
/src/components/layout/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 后台管理系统
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/npm/element-plus-admin/components/file-viewer/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/npm/element-plus-admin/components/login/components/main/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
{{ tip }}
6 |

7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/npm/element-plus-admin/assets/styles/classes/base-colors.scss:
--------------------------------------------------------------------------------
1 | @each $key, $value in $base-colors {
2 | .t-#{"" + $key} {
3 | color: $value !important;
4 | }
5 |
6 | .bg-#{"" + $key} {
7 | background-color: $value !important;
8 | }
9 |
10 | .bd-#{"" + $key} {
11 | border: 1px solid $value !important;
12 | }
13 |
14 | .bdb-#{"" + $key} {
15 | border-bottom: 1px solid $value !important;
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/composables/use-consts.js:
--------------------------------------------------------------------------------
1 | export const useConsts = () => {
2 | // 基础地址
3 | const BaseUrl = process.env["BASE_URL"];
4 |
5 | // 接口地址
6 | const ApiUrl = process.env["VUE_APP_API_URL"];
7 |
8 | // 静态文件地址
9 | const StaticUrl = process.env["VUE_APP_STATIC_URL"];
10 |
11 | // 分页大小
12 | const PageSize = 10;
13 |
14 | return {
15 | BaseUrl,
16 | ApiUrl,
17 | StaticUrl,
18 | PageSize,
19 | };
20 | };
21 |
--------------------------------------------------------------------------------
/npm/element-plus-admin/composables/use-xlsx.js:
--------------------------------------------------------------------------------
1 | import * as Xlsx from "xlsx";
2 |
3 | export const useXlsx = () => {
4 | const save = ({ fileName, data } = {}) => {
5 | const worksheet = Xlsx.utils.json_to_sheet(data);
6 | const workbook = Xlsx.utils.book_new();
7 |
8 | Xlsx.utils.book_append_sheet(workbook, worksheet, "Sheet");
9 | Xlsx.writeFile(workbook, `${fileName}.xlsx`);
10 | };
11 |
12 | return {
13 | save,
14 | };
15 | };
16 |
--------------------------------------------------------------------------------
/npm/element-plus-admin/components/items/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | {{ (items || []).map((item) => item[labelKey]).join(joinString) }}
4 |
5 |
6 | -
11 | {{ item[labelKey] }}
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/npm/element-plus-admin/components/enum-select/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
10 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/src/views/orders/list/components/detail/style.scss:
--------------------------------------------------------------------------------
1 | @use "sass:map";
2 |
3 | @import "~@/assets/styles/utils";
4 |
5 | @include b(detail) {
6 | max-height: 450px;
7 | overflow-x: hidden;
8 | overflow-y: auto;
9 | padding-right: 4px;
10 |
11 | li {
12 | @include padding(6px, null, 6px, 90px);
13 |
14 | position: relative;
15 | line-height: 150%;
16 | }
17 |
18 | label {
19 | @include position--absolute(10px, null, null, 0);
20 |
21 | line-height: 100%;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/npm/element-plus-admin/assets/styles/global/components/scrollbar.scss:
--------------------------------------------------------------------------------
1 | @use "sass:map";
2 |
3 | @include c(scrollbar) {
4 | &::-webkit-scrollbar {
5 | width: 10px;
6 | height: 10px;
7 | background-color: white;
8 | }
9 |
10 | &::-webkit-scrollbar-thumb {
11 | background-color: map.get($border-color, light);
12 | border-radius: 5px;
13 | }
14 |
15 | &::-webkit-scrollbar-track {
16 | background-color: map.get($border-color, extra-light);
17 | border-radius: 5px;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/npm/element-plus-admin/components/editor/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/src/views/login/components/form/style.scss:
--------------------------------------------------------------------------------
1 | @import "~@/assets/styles/utils";
2 |
3 | @include b(form) {
4 | width: 280px;
5 |
6 | .el-form-item {
7 | margin-bottom: 24px;
8 | }
9 |
10 | @include e(wrap) {
11 | @include position--absolute(50%, 50px);
12 | @include padding(44px, 36px, 24px, 36px);
13 |
14 | transform: translateY(-50%);
15 | background: white;
16 | }
17 |
18 | @include e(title) {
19 | padding-bottom: 30px;
20 | line-height: 100%;
21 | font-size: 20px;
22 | text-align: center;
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/store/index.js:
--------------------------------------------------------------------------------
1 | import { createStore } from "vuex";
2 | import createPersistedState from "vuex-persistedstate";
3 | import storage from "jt-storage";
4 | import modules from "./modules";
5 |
6 | export const store = createStore({
7 | plugins: [
8 | createPersistedState({
9 | paths: ["enums", "auth"],
10 | storage: {
11 | getItem: (key) => storage.get(key),
12 | setItem: (key, value) => storage.set(key, value),
13 | removeItem: (key) => storage.remove(key),
14 | },
15 | }),
16 | ],
17 | modules,
18 | });
19 |
--------------------------------------------------------------------------------
/npm/element-plus-admin/components/file-viewer/components/video-viewer/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/npm/element-plus-admin/components/layout/header/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/src/store/enums.js:
--------------------------------------------------------------------------------
1 | import { defineStore } from "pinia/dist/pinia.cjs";
2 | import { ref } from "vue";
3 | import { publicEnumsApi } from "element-plus-admin/apis/public/enums";
4 |
5 | export const useEnumsStore = defineStore(
6 | "enums",
7 | () => {
8 | const data = ref({
9 | config: {
10 | version: "",
11 | },
12 | });
13 |
14 | const get = async () => {
15 | data.value = await publicEnumsApi.get({});
16 | };
17 |
18 | return {
19 | data,
20 | get,
21 | };
22 | },
23 | { persist: true }
24 | );
25 |
--------------------------------------------------------------------------------
/npm/element-plus-admin/components/file-list/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
8 | {{ file.name }}
9 |
10 |
11 |
12 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/src/views/login/script.js:
--------------------------------------------------------------------------------
1 | import CLogin from "element-plus-admin/components/login/index.vue";
2 | import CLoginHeader from "element-plus-admin/components/login/components/header/index.vue";
3 | import CLoginMain from "element-plus-admin/components/login/components/main/index.vue";
4 | import CLoginFooter from "element-plus-admin/components/login/components/footer/index.vue";
5 | import BForm from "./components/form/index.vue";
6 |
7 | export default {
8 | components: {
9 | CLogin,
10 | CLoginHeader,
11 | CLoginMain,
12 | CLoginFooter,
13 | BForm,
14 | },
15 | };
16 |
--------------------------------------------------------------------------------
/npm/element-plus-admin/assets/styles/element-plus/components.scss:
--------------------------------------------------------------------------------
1 | @import "~sass-utils";
2 |
3 | @include c(date-picker) {
4 | @include m(full) {
5 | width: 100% !important;
6 | }
7 |
8 | @include m(two-days) {
9 | width: 360px !important;
10 | }
11 | }
12 |
13 | @include c(input-number) {
14 | @include m(full) {
15 | width: 100% !important;
16 | }
17 | }
18 |
19 | @include c(select) {
20 | @include m(full) {
21 | width: 100% !important;
22 | }
23 | }
24 |
25 | @include c(button) {
26 | @include m(full) {
27 | width: 100% !important;
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/views/orders/list/components/detail/script.js:
--------------------------------------------------------------------------------
1 | import { reactive, ref } from "vue";
2 | import { useEnums } from "element-plus-admin/composables/use-enums";
3 |
4 | export default {
5 | setup() {
6 | const { enums } = useEnums();
7 |
8 | const cDialog = reactive({
9 | visible: false,
10 | });
11 |
12 | const detail = ref({});
13 |
14 | const show = (data) => {
15 | detail.value = data;
16 | cDialog.visible = true;
17 | };
18 |
19 | return {
20 | enums,
21 | cDialog,
22 | detail,
23 | show,
24 | };
25 | },
26 | };
27 |
--------------------------------------------------------------------------------
/src/assets/styles/utils/variables.scss:
--------------------------------------------------------------------------------
1 | @use "sass:map";
2 |
3 | @import "../element-plus/variables";
4 |
5 | $font-sizes: $pxes;
6 |
7 | $base-colors: (
8 | white: white,
9 | black: black,
10 | primary: map.get($colors, primary, base),
11 | success: map.get($colors, success, base),
12 | warning: map.get($colors, warning, base),
13 | danger: map.get($colors, danger, base),
14 | info: map.get($colors, info, base),
15 | secondary: #409eff,
16 | );
17 |
18 | $the-layout: (
19 | the-header: (
20 | height: 60px,
21 | ),
22 | the-sidebar: (
23 | width: 210px,
24 | ),
25 | );
26 |
--------------------------------------------------------------------------------
/npm/element-plus-admin/components/file-viewer/components/office-viewer/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/npm/element-plus-admin/components/file-viewer/components/video-viewer/script.js:
--------------------------------------------------------------------------------
1 | import { reactive, watch } from "vue";
2 |
3 | export default {
4 | setup() {
5 | const cDialog = reactive({
6 | visible: false,
7 | src: "",
8 | });
9 |
10 | watch(
11 | () => cDialog.visible,
12 | (newVal) => {
13 | if (!newVal) {
14 | cDialog.src = "";
15 | }
16 | }
17 | );
18 |
19 | const show = ({ src }) => {
20 | cDialog.visible = true;
21 | cDialog.src = src;
22 | };
23 |
24 | return {
25 | cDialog,
26 | show,
27 | };
28 | },
29 | };
30 |
--------------------------------------------------------------------------------
/npm/element-plus-admin/composables/use-enums.js:
--------------------------------------------------------------------------------
1 | import { computed } from "vue";
2 | import { useStore } from "vuex";
3 | import { publicEnumsApi } from "../apis/public/enums";
4 |
5 | export const useEnums = () => {
6 | const { dispatch, state } = useStore();
7 |
8 | const enums = computed(() => state["enums"].data);
9 |
10 | const getEnums = async () => {
11 | const { version } = await publicEnumsApi.post({
12 | action: "getVersion",
13 | });
14 |
15 | if (version !== enums.value.config.version) {
16 | await dispatch("enums/get");
17 | }
18 | };
19 |
20 | return { enums, getEnums };
21 | };
22 |
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | 后台管理系统
9 |
10 |
11 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/npm/element-plus-admin/components/login/components/header/style.scss:
--------------------------------------------------------------------------------
1 | @use "sass:map";
2 |
3 | @import "~@/assets/styles/utils";
4 |
5 | @include b(header) {
6 | @include position--fixed(0, null, null, 0);
7 | @include size(100%, 80px);
8 |
9 | @include c(container) {
10 | height: 100%;
11 | }
12 |
13 | @include e(logo) {
14 | @include position--absolute(50%, null, null, 0);
15 | @include text--middle(50px);
16 |
17 | transform: translateY(-50%);
18 | padding-left: 60px;
19 |
20 | img {
21 | @include position--absolute(0, null, null, 0);
22 | @include size(50px, 50px);
23 |
24 | display: block;
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/views/login/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | 煊赫生活(厦门)传媒有限公司
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | 煊赫生活(厦门)传媒有限公司 xmxhsh.cn
17 | 闽ICP备09078326号
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
30 |
--------------------------------------------------------------------------------
/npm/element-plus-admin/store/modules/enums.js:
--------------------------------------------------------------------------------
1 | import helpers from "jt-helpers";
2 | import { publicEnumsApi } from "../../apis/public/enums";
3 |
4 | const state = {
5 | data: {
6 | config: {
7 | version: "",
8 | },
9 | },
10 | };
11 |
12 | const types = helpers.keyMirror({
13 | SetData: null,
14 | });
15 |
16 | const mutations = {
17 | [types.SetData](state, data) {
18 | state.data = data;
19 | },
20 | };
21 |
22 | const actions = {
23 | async get({ commit }) {
24 | const res = await publicEnumsApi.get({});
25 | commit(types.SetData, res);
26 | return res;
27 | },
28 | };
29 |
30 | export const enums = {
31 | namespaced: true,
32 | state,
33 | mutations,
34 | actions,
35 | };
36 |
--------------------------------------------------------------------------------
/npm/element-plus-admin/composables/use-auth.js:
--------------------------------------------------------------------------------
1 | import { computed } from "vue";
2 | import { store } from "@/store";
3 |
4 | export const useAuth = () => {
5 | const { dispatch, state } = store;
6 |
7 | const user = computed(() => state.auth.user);
8 |
9 | const menus = computed(() => state.auth.menus);
10 |
11 | const getHeaders = () => {
12 | return { Authorization: `Bearer ${user.value.token}` };
13 | };
14 |
15 | const loggedIn = () => !!user.value.token;
16 |
17 | const login = (options) => dispatch("auth/login", options);
18 |
19 | const logout = () => dispatch("auth/logout");
20 |
21 | return {
22 | user,
23 | menus,
24 | getHeaders,
25 | loggedIn,
26 | login,
27 | logout,
28 | };
29 | };
30 |
--------------------------------------------------------------------------------
/npm/element-plus-admin/components/login/components/main/style.scss:
--------------------------------------------------------------------------------
1 | @use "sass:map";
2 |
3 | @import "~@/assets/styles/utils";
4 |
5 | @include b(main) {
6 | @include position--fixed(80px, null, 80px, 0);
7 |
8 | width: 100%;
9 | background: {
10 | image: url("./components/main/images/bg.png");
11 | position: center center;
12 | size: auto 100%;
13 | }
14 |
15 | @include c(container) {
16 | height: 100%;
17 | }
18 |
19 | @include e(icon) {
20 | @include position--absolute(50%, null, null, 0);
21 |
22 | transform: translateY(-50%);
23 |
24 | h2 {
25 | padding-top: 50px;
26 | }
27 |
28 | img {
29 | @include size(520px * 0.85, 446px * 0.85);
30 |
31 | display: block;
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/npm/element-plus-admin/components/enum-select/script.js:
--------------------------------------------------------------------------------
1 | export default {
2 | props: {
3 | placeholder: {
4 | type: String,
5 | default: "请选择",
6 | },
7 | multiple: {
8 | type: Boolean,
9 | default: false,
10 | },
11 | clearable: {
12 | type: Boolean,
13 | default: false,
14 | },
15 | items: {
16 | type: Object,
17 | default: () => [],
18 | },
19 | value: {
20 | type: [String, Number],
21 | },
22 | },
23 | emits: ["update:value", "change"],
24 | setup(props, context) {
25 | const onChange = (value) => {
26 | context.emit("update:value", value);
27 | context.emit("change", value);
28 | };
29 |
30 | return {
31 | onChange,
32 | };
33 | },
34 | };
35 |
--------------------------------------------------------------------------------
/src/main.js:
--------------------------------------------------------------------------------
1 | import { createApp } from "vue";
2 | import { createPinia } from "pinia";
3 | import piniaPluginPersistedstate from "pinia-plugin-persistedstate";
4 | import "@/assets/styles/element-plus/index.scss";
5 | import ElementPlus from "element-plus";
6 | import "@/assets/styles/global/index.scss";
7 | import "element-plus-admin/utils/polyfills";
8 | import { config } from "@/utils/config";
9 | import { useComponents } from "@/utils/use-components";
10 | import App from "./App.vue";
11 | import { store } from "./store";
12 | import { router } from "./router";
13 |
14 | const pinia = createPinia();
15 | const app = createApp(App);
16 |
17 | pinia.use(piniaPluginPersistedstate);
18 |
19 | app.use(pinia).use(store).use(router).use(ElementPlus).mount("#app");
20 |
21 | config(app);
22 | useComponents(app);
23 |
--------------------------------------------------------------------------------
/npm/element-plus-admin/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "element-plus-admin",
3 | "version": "1.2.17",
4 | "description": "element-plus admin system",
5 | "main": "index.js",
6 | "author": "zhaojintian",
7 | "license": "ISC",
8 | "dependencies": {
9 | "@element-plus/icons-vue": "^2.1.0",
10 | "@wangeditor/editor": "^5.1.23",
11 | "@wangeditor/editor-for-vue": "^5.1.12",
12 | "ali-oss": "^6.17.1",
13 | "axios": "^1.4.0",
14 | "core-js": "^3.31.0",
15 | "cos-js-sdk-v5": "^1.4.18",
16 | "element-plus": "^2.3.7",
17 | "js-base64": "^3.7.5",
18 | "nprogress": "^0.2.0",
19 | "sass-utils": "^1.1.12",
20 | "throttle-debounce": "^5.0.0",
21 | "vue": "^3.3.4",
22 | "vue-router": "^4.2.2",
23 | "wangeditor": "^4.7.15",
24 | "xlsx": "^0.18.5"
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/npm/element-plus-admin/components/list/composables/use-helpers.js:
--------------------------------------------------------------------------------
1 | import { Base64 } from "js-base64";
2 |
3 | export const useHelpers = () => {
4 | const formatFilters = (filters) => {
5 | delete filters.isTrusted;
6 |
7 | const ret = {};
8 |
9 | Object.keys(filters).forEach((key) => {
10 | if (
11 | typeof filters[key] === "string" ||
12 | typeof filters[key] === "number"
13 | ) {
14 | ret[key] = { $eq: filters[key] };
15 | } else {
16 | ret[key] = filters[key];
17 | }
18 | });
19 |
20 | return ret;
21 | };
22 |
23 | const encode = (object) => Base64.encode(JSON.stringify(object));
24 |
25 | const decode = (string) =>
26 | string ? JSON.parse(Base64.decode(string.toString())) : {};
27 |
28 | return { formatFilters, encode, decode };
29 | };
30 |
--------------------------------------------------------------------------------
/npm/element-plus-admin/components/order-input/script.js:
--------------------------------------------------------------------------------
1 | import { debounce } from "lodash";
2 |
3 | export default {
4 | props: {
5 | row: Object,
6 | api: Object,
7 | where: {
8 | type: Object,
9 | default: () => ({}),
10 | },
11 | },
12 | emits: ["ok"],
13 | setup(props, context) {
14 | const onChange = debounce(async (value) => {
15 | await props.api.post({
16 | joinUrl: `/${props.row.id}/actions/order`,
17 | query: {
18 | where: {
19 | id: { $ne: 0 },
20 | ...props.where,
21 | },
22 | },
23 | body: {
24 | action: "Update",
25 | order: value,
26 | },
27 | });
28 |
29 | context.emit("ok");
30 | }, 500);
31 |
32 | return {
33 | onChange,
34 | };
35 | },
36 | };
37 |
--------------------------------------------------------------------------------
/src/store/user.js:
--------------------------------------------------------------------------------
1 | import { defineStore } from "pinia/dist/pinia.cjs";
2 | import { publicManagersApi } from "element-plus-admin/apis/public/managers";
3 | import { ref } from "vue";
4 |
5 | export const useUserStore = defineStore(
6 | "user",
7 | () => {
8 | const user = ref({});
9 | const token = ref("");
10 |
11 | const login = async (data) => {
12 | const res = await publicManagersApi.post({ action: "login", body: data });
13 |
14 | user.value = { id: res.manager.id, name: res.manager.username };
15 | token.value = res.token;
16 | };
17 |
18 | const logout = () => {
19 | user.value = {};
20 | token.value = "";
21 | };
22 |
23 | return {
24 | user,
25 | token,
26 | login,
27 | logout,
28 | };
29 | },
30 | {
31 | persist: true,
32 | }
33 | );
34 |
--------------------------------------------------------------------------------
/src/views/users/list/script.js:
--------------------------------------------------------------------------------
1 | import { useList } from "element-plus-admin/components/list/composables/use-list";
2 | import { useEnums } from "element-plus-admin/composables/use-enums";
3 | import { usersApi } from "@/apis/admin/users";
4 |
5 | export default {
6 | setup() {
7 | const { enums } = useEnums();
8 |
9 | const { list, currentPage, cFilters, reRender, onPageChange, search } =
10 | useList({
11 | api: usersApi,
12 | filters: {
13 | model: {
14 | nickName: { $like: "" },
15 | phoneNumber: { $like: "" },
16 | },
17 | rules: {},
18 | },
19 | });
20 |
21 | return {
22 | usersApi,
23 | enums,
24 | list,
25 | currentPage,
26 | cFilters,
27 | reRender,
28 | onPageChange,
29 | search,
30 | };
31 | },
32 | };
33 |
--------------------------------------------------------------------------------
/src/router/routes/private/index.js:
--------------------------------------------------------------------------------
1 | import { adsRoute } from "./ads";
2 | import { appUpgradesRoute } from "./app-upgrades";
3 | import { categoriesRoute } from "./categories";
4 | import { homeRoute } from "./home";
5 | import { ordersRoute } from "./orders";
6 | import { productsRoute } from "./products";
7 | import { usersRoute } from "./users";
8 |
9 | import { articlesRoute } from "./articles";
10 | import { contentRoute } from "./contents";
11 | import { jobsRoute } from "./jobs";
12 | import { researchesRoute } from "./researches";
13 | import { teamMembersRoute } from "./team-members";
14 |
15 | export const privateRoutes = [
16 | adsRoute,
17 | appUpgradesRoute,
18 | categoriesRoute,
19 | homeRoute,
20 | ordersRoute,
21 | productsRoute,
22 | usersRoute,
23 |
24 | articlesRoute,
25 | contentRoute,
26 | jobsRoute,
27 | researchesRoute,
28 | teamMembersRoute,
29 | ];
30 |
--------------------------------------------------------------------------------
/src/views/contents/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
18 |
19 |
20 |
21 | 保存
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/npm/element-plus-admin/components/file-viewer/components/office-viewer/script.js:
--------------------------------------------------------------------------------
1 | import { reactive, watch } from "vue";
2 |
3 | export default {
4 | props: {
5 | serviceUrl: String,
6 | },
7 | setup(props) {
8 | const cDialog = reactive({
9 | visible: false,
10 | serviceUrl:
11 | props.serviceUrl ||
12 | "https://view.officeapps.live.com/op/view.aspx?src=",
13 | src: "",
14 | });
15 |
16 | watch(
17 | () => cDialog.visible,
18 | (newVal) => {
19 | if (!newVal) {
20 | cDialog.src = "";
21 | }
22 | }
23 | );
24 |
25 | const show = ({ serviceUrl, src }) => {
26 | cDialog.visible = true;
27 |
28 | if (serviceUrl) {
29 | cDialog.serviceUrl = serviceUrl;
30 | }
31 |
32 | cDialog.src = src;
33 | };
34 |
35 | return {
36 | cDialog,
37 | show,
38 | };
39 | },
40 | };
41 |
--------------------------------------------------------------------------------
/src/components/layout/script.js:
--------------------------------------------------------------------------------
1 | import TheHeader from "element-plus-admin/components/layout/header/index.vue";
2 | import TheSidebar from "element-plus-admin/components/layout/sidebar/index.vue";
3 | import TheMain from "element-plus-admin/components/layout/main/index.vue";
4 | import { ElMessage } from "element-plus";
5 | import { useRouter } from "vue-router";
6 | import { useAuth } from "element-plus-admin/composables/use-auth";
7 | import { getMenus } from "./utils/get-menus";
8 |
9 | export default {
10 | components: {
11 | TheHeader,
12 | TheSidebar,
13 | TheMain,
14 | },
15 | setup() {
16 | const router = useRouter();
17 | const { user, logout: userLogout } = useAuth();
18 |
19 | const logout = async () => {
20 | await userLogout();
21 | await router.push("/login");
22 | ElMessage.success("退出成功");
23 | };
24 |
25 | return {
26 | user,
27 | getMenus,
28 | logout,
29 | };
30 | },
31 | };
32 |
--------------------------------------------------------------------------------
/npm/element-plus-admin/components/resource-select/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
11 |
21 |
31 |
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/npm/element-plus-admin/components/list/style.scss:
--------------------------------------------------------------------------------
1 | @import "~@/assets/styles/utils";
2 |
3 | @include c(list) {
4 | @import "./components/image/style";
5 |
6 | @include e(header) {
7 | @include clearfix;
8 |
9 | .el-form-item {
10 | margin-bottom: 18px !important;
11 | }
12 |
13 | @include s(is-simple) {
14 | @include c(list) {
15 | @include e(operations) {
16 | margin-bottom: 18px;
17 | float: left;
18 | }
19 |
20 | @include e(filters) {
21 | float: right;
22 | margin-right: -10px;
23 | }
24 | }
25 | }
26 | }
27 |
28 | @include e(operations) {
29 | margin-bottom: 10px;
30 | }
31 |
32 | @include e(filters) {
33 | .el-select,
34 | .el-input {
35 | width: 200px !important;
36 | }
37 | }
38 |
39 | @include e(pagination) {
40 | padding-top: 10px;
41 | position: relative;
42 | text-align: right;
43 | right: -9px;
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/npm/element-plus-admin/components/layout/header/style.scss:
--------------------------------------------------------------------------------
1 | @use "sass:map";
2 |
3 | @import "~@/assets/styles/utils";
4 |
5 | @include c(header) {
6 | @include text--middle(map.get($the-layout, the-header, height));
7 |
8 | position: relative;
9 | background-color: white;
10 |
11 | @include e(logo) {
12 | @include position--absolute(50%, null, null, 20px);
13 | @include text--middle(36px);
14 |
15 | transform: translateY(-50%);
16 | padding-left: 46px;
17 | font-size: 20px;
18 | font-weight: bold;
19 | cursor: pointer;
20 |
21 | img {
22 | @include position--absolute(0, null, null, 0);
23 | @include size(36px, 36px);
24 |
25 | display: block;
26 | }
27 | }
28 |
29 | @include e(user) {
30 | @include position--absolute(0, 20px);
31 | @include text--middle(map.get($the-layout, the-header, height));
32 |
33 | padding-right: 50px;
34 |
35 | .el-avatar {
36 | @include position--absolute(50%, 0);
37 |
38 | transform: translateY(-50%);
39 | cursor: pointer;
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/utils/use-components.js:
--------------------------------------------------------------------------------
1 | import CEditor from "element-plus-admin/components/editor";
2 | import CEnumSelect from "element-plus-admin/components/enum-select/index.vue";
3 | import CList from "element-plus-admin/components/list/index.vue";
4 | import CListImage from "element-plus-admin/components/list/components/image/index.vue";
5 | import COrderInput from "element-plus-admin/components/order-input/index.vue";
6 | import CResourceSelect from "element-plus-admin/components/resource-select/index.vue";
7 | import CUpload from "element-plus-admin/components/upload/index.vue";
8 | import { Eleme, Right } from "@element-plus/icons-vue";
9 |
10 | export const useComponents = (app) => {
11 | app.component("c-editor", CEditor);
12 | app.component("c-enum-select", CEnumSelect);
13 | app.component("c-list", CList);
14 | app.component("c-list-image", CListImage);
15 | app.component("c-order-input", COrderInput);
16 | app.component("c-resource-select", CResourceSelect);
17 | app.component("c-upload", CUpload);
18 | app.component("el-icon-right", Right);
19 | app.component("el-icon-eleme", Eleme);
20 | };
21 |
--------------------------------------------------------------------------------
/src/views/ads/list/script.js:
--------------------------------------------------------------------------------
1 | import { useList } from "element-plus-admin/components/list/composables/use-list";
2 | import { useEnums } from "element-plus-admin/composables/use-enums";
3 | import { ref } from "vue";
4 | import { adsApi } from "@/apis/admin/ads";
5 | import BForm from "./components/form/index.vue";
6 |
7 | export default {
8 | components: {
9 | BForm,
10 | },
11 | setup() {
12 | const formRef = ref(null);
13 |
14 | const { enums } = useEnums();
15 |
16 | const { list, currentPage, cFilters, reRender, onPageChange, search, del } =
17 | useList({
18 | api: adsApi,
19 | filters: {
20 | model: {
21 | title: { $like: "" },
22 | },
23 | rules: {},
24 | },
25 | extraQuery: {
26 | order: [["order", "DESC"]],
27 | },
28 | });
29 |
30 | return {
31 | adsApi,
32 | formRef,
33 | enums,
34 | list,
35 | currentPage,
36 | cFilters,
37 | reRender,
38 | onPageChange,
39 | search,
40 | del,
41 | };
42 | },
43 | };
44 |
--------------------------------------------------------------------------------
/src/views/jobs/list/script.js:
--------------------------------------------------------------------------------
1 | import { useList } from "element-plus-admin/components/list/composables/use-list";
2 | import { useEnums } from "element-plus-admin/composables/use-enums";
3 | import { ref } from "vue";
4 | import { jobsApi } from "@/apis/admin/jobs";
5 | import BForm from "./components/form/index.vue";
6 |
7 | export default {
8 | components: {
9 | BForm,
10 | },
11 | setup() {
12 | const formRef = ref(null);
13 |
14 | const { enums } = useEnums();
15 |
16 | const { list, currentPage, cFilters, reRender, onPageChange, search, del } =
17 | useList({
18 | api: jobsApi,
19 | filters: {
20 | model: {
21 | name: { $like: "" },
22 | },
23 | rules: {},
24 | },
25 | extraQuery: {
26 | order: [["order", "DESC"]],
27 | },
28 | });
29 |
30 | return {
31 | jobsApi,
32 | formRef,
33 | enums,
34 | list,
35 | currentPage,
36 | cFilters,
37 | reRender,
38 | onPageChange,
39 | search,
40 | del,
41 | };
42 | },
43 | };
44 |
--------------------------------------------------------------------------------
/src/views/ads/list/components/form/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
7 |
14 |
15 |
16 |
17 |
18 |
26 |
27 |
28 |
29 | 取消
30 | 确定
31 |
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/npm/element-plus-admin/components/upload/files/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
10 |
11 |
12 |
13 |
{{ file.name }}
14 |
15 |
16 |
17 |
21 |
22 |
23 |
24 |
25 |
26 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/npm/element-plus-admin/components/upload/files/style.scss:
--------------------------------------------------------------------------------
1 | @use "sass:map";
2 |
3 | @import "~@/assets/styles/utils";
4 |
5 | @include cc(files) {
6 | padding-top: 4px;
7 |
8 | @include e(item) {
9 | @include text--middle(32px);
10 |
11 | position: relative;
12 | border-radius: 4px;
13 | cursor: pointer;
14 |
15 | &:hover {
16 | background-color: #f5f7fa;
17 |
18 | @include cc(files) {
19 | @include e(status) {
20 | display: none;
21 | }
22 |
23 | @include e(close) {
24 | display: block;
25 | }
26 | }
27 | }
28 | }
29 |
30 | @include e(icon, status, close, name) {
31 | @include position--absolute(50%);
32 |
33 | transform: translateY(-50%);
34 | }
35 |
36 | @include e(icon) {
37 | left: 10px;
38 | }
39 |
40 | @include e(status, close) {
41 | right: 10px;
42 | }
43 |
44 | @include e(status) {
45 | color: map.get($colors, success, base) !important;
46 | }
47 |
48 | @include e(close) {
49 | display: none;
50 | }
51 |
52 | @include e(name) {
53 | left: 30px;
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/views/categories/list/components/form/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
7 |
14 |
15 |
16 |
17 |
18 |
26 |
27 |
28 |
29 | 取消
30 | 确定
31 |
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/src/views/orders/list/script.js:
--------------------------------------------------------------------------------
1 | import { useList } from "element-plus-admin/components/list/composables/use-list";
2 | import { useEnums } from "element-plus-admin/composables/use-enums";
3 | import { ordersApi } from "@/apis/admin/orders";
4 | import BDetail from "./components/detail/index.vue";
5 | import { ref } from "vue";
6 |
7 | export default {
8 | components: {
9 | BDetail,
10 | },
11 | setup() {
12 | const { enums } = useEnums();
13 |
14 | const { list, currentPage, cFilters, reRender, onPageChange, search, del } =
15 | useList({
16 | api: ordersApi,
17 | filters: {
18 | model: {
19 | no: { $like: "" },
20 | },
21 | rules: {},
22 | },
23 | extraQuery: {
24 | include: [{ model: "User", as: "user" }],
25 | },
26 | });
27 |
28 | const detail = ref(null);
29 |
30 | return {
31 | ordersApi,
32 | enums,
33 | list,
34 | currentPage,
35 | cFilters,
36 | detail,
37 | reRender,
38 | onPageChange,
39 | search,
40 | del,
41 | };
42 | },
43 | };
44 |
--------------------------------------------------------------------------------
/src/router/index.js:
--------------------------------------------------------------------------------
1 | import { createRouter, createWebHashHistory } from "vue-router";
2 | import { publicRoutes } from "./routes/public";
3 | import { privateRoutes } from "./routes/private";
4 | import { store } from "@/store";
5 |
6 | const routes = [
7 | {
8 | path: "/",
9 | component: () => import("@/components/layout/index.vue"),
10 | children: privateRoutes,
11 | meta: {
12 | requiresAuth: true,
13 | },
14 | },
15 | ...publicRoutes,
16 | {
17 | path: "/:matchOthers(.*)*",
18 | component: () => import("@/views/404/index.vue"),
19 | },
20 | ];
21 |
22 | const router = createRouter({
23 | history: createWebHashHistory(),
24 | routes,
25 | });
26 |
27 | router.beforeEach(async (to, from, next) => {
28 | if (to.matched.some((record) => record.meta.requiresAuth)) {
29 | if (!store.state.auth.user.token) {
30 | await store.dispatch("auth/logout");
31 |
32 | next({
33 | path: "login",
34 | query: { redirect: to.fullPath },
35 | });
36 | } else {
37 | next();
38 | }
39 | } else {
40 | next();
41 | }
42 | });
43 |
44 | export { router };
45 |
--------------------------------------------------------------------------------
/src/views/app-upgrades/list/script.js:
--------------------------------------------------------------------------------
1 | import { useList } from "element-plus-admin/components/list/composables/use-list";
2 | import { useEnums } from "element-plus-admin/composables/use-enums";
3 | import { ref } from "vue";
4 | import { appUpgradesApi } from "@/apis/admin/app-upgrades";
5 | import BForm from "./components/form/index.vue";
6 |
7 | export default {
8 | components: {
9 | BForm,
10 | },
11 | setup() {
12 | const formRef = ref(null);
13 |
14 | const { enums } = useEnums();
15 |
16 | const { list, currentPage, cFilters, reRender, onPageChange, search, del } =
17 | useList({
18 | api: appUpgradesApi,
19 | filters: {
20 | model: {
21 | versionName: { $like: "" },
22 | versionCode: { $like: "" },
23 | status: undefined,
24 | },
25 | rules: {},
26 | },
27 | });
28 |
29 | return {
30 | appUpgradesApi,
31 | formRef,
32 | enums,
33 | list,
34 | currentPage,
35 | cFilters,
36 | reRender,
37 | onPageChange,
38 | search,
39 | del,
40 | };
41 | },
42 | };
43 |
--------------------------------------------------------------------------------
/npm/element-plus-admin/store/modules/items.js:
--------------------------------------------------------------------------------
1 | import { keyMirror } from "jt-helpers";
2 |
3 | const state = {
4 | data: {},
5 | };
6 |
7 | const types = keyMirror({
8 | SetData: null,
9 | });
10 |
11 | const mutations = {
12 | [types.SetData](state, { resource, key, items }) {
13 | state.data[resource] = {
14 | ...state.data[resource],
15 | [key]: items,
16 | };
17 | },
18 | };
19 |
20 | const actions = {
21 | async getItems({ state, commit }, { resource, api, ids }) {
22 | const key = ids ? ids.join(",") : "__";
23 |
24 | if (state.data[resource] && state.data[resource][key]) {
25 | return state.data[resource][key];
26 | } else {
27 | state.data[resource] = {
28 | ...state.data[resource],
29 | [key]: [],
30 | };
31 |
32 | const { items } = await api.post({
33 | action: "getAllByIds",
34 | body: { ids },
35 | });
36 |
37 | commit(types.SetData, { resource, key, items });
38 |
39 | return items;
40 | }
41 | },
42 | };
43 |
44 | export const items = {
45 | namespaced: true,
46 | state,
47 | mutations,
48 | actions,
49 | };
50 |
--------------------------------------------------------------------------------
/src/views/categories/list/script.js:
--------------------------------------------------------------------------------
1 | import { useList } from "element-plus-admin/components/list/composables/use-list";
2 | import { useEnums } from "element-plus-admin/composables/use-enums";
3 | import { ref } from "vue";
4 | import { categoriesApi } from "@/apis/admin/categories";
5 | import BForm from "./components/form/index.vue";
6 |
7 | export default {
8 | components: {
9 | BForm,
10 | },
11 | setup() {
12 | const formRef = ref(null);
13 |
14 | const { enums } = useEnums();
15 |
16 | const { list, currentPage, cFilters, reRender, onPageChange, search, del } =
17 | useList({
18 | api: categoriesApi,
19 | filters: {
20 | model: {
21 | name: { $like: "" },
22 | cnName: { $like: "" },
23 | },
24 | rules: {},
25 | },
26 | extraQuery: {
27 | order: [["order", "ASC"]],
28 | },
29 | });
30 |
31 | return {
32 | categoriesApi,
33 | formRef,
34 | enums,
35 | list,
36 | currentPage,
37 | cFilters,
38 | reRender,
39 | onPageChange,
40 | search,
41 | del,
42 | };
43 | },
44 | };
45 |
--------------------------------------------------------------------------------
/npm/element-plus-admin/components/layout/main/style.scss:
--------------------------------------------------------------------------------
1 | @use "sass:map";
2 |
3 | @import "~@/assets/styles/utils";
4 |
5 | @include c(main) {
6 | @include position--absolute(
7 | map.get($the-layout, the-header, height) + 14px,
8 | 0,
9 | 14px,
10 | map.get($the-layout, the-sidebar, width) + 14px * 2
11 | );
12 |
13 | overflow: hidden;
14 | min-width: 1200px;
15 |
16 | @include e(body) {
17 | @include position--absolute(0, 14px, 0, 0);
18 | @include padding(50px + 12px, 20px, 20px, 20px);
19 |
20 | background-color: white;
21 | overflow-x: hidden;
22 | overflow-y: auto;
23 |
24 | &::-webkit-scrollbar {
25 | width: 10px;
26 | background-color: white;
27 | }
28 |
29 | &::-webkit-scrollbar-track {
30 | background-color: white;
31 | }
32 |
33 | &::-webkit-scrollbar-thumb {
34 | background-color: #f6f7fb;
35 | }
36 | }
37 |
38 | @include e(breadcrumb) {
39 | @include position--absolute(0, 14px + 20px, null, 20px);
40 | @include text--middle(50px);
41 |
42 | background-color: white;
43 | border-bottom: 1px solid map.get($border-color, light);
44 | z-index: 1;
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/npm/element-plus-admin/components/list/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
15 |
16 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/npm/element-plus-admin/components/upload/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
14 |
15 | {{ placeholder }}
16 |
17 |
18 | {{ tip }}
19 |
24 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/npm/element-plus-admin/components/layout/sidebar/style.scss:
--------------------------------------------------------------------------------
1 | @use "sass:map";
2 |
3 | @import "~@/assets/styles/utils";
4 |
5 | @include c(sidebar) {
6 | @include position--absolute(
7 | map.get($the-layout, the-header, height) + 14px,
8 | null,
9 | 14px,
10 | 14px
11 | );
12 |
13 | z-index: 1;
14 | width: map.get($the-layout, the-sidebar, width);
15 | background-color: map.get($base-colors, primary);
16 | overflow-x: hidden;
17 | overflow-y: auto;
18 |
19 | &::-webkit-scrollbar {
20 | width: 0;
21 | }
22 |
23 | .el-menu {
24 | width: map.get($the-layout, the-sidebar, width) + 2px;
25 | }
26 |
27 | .el-sub-menu__title,
28 | .el-menu > .el-menu-item {
29 | @include c(iconfont) {
30 | font-size: 16px;
31 | color: white;
32 | margin-right: 10px;
33 | }
34 | }
35 |
36 | .el-sub-menu .el-menu-item {
37 | padding-left: 46px !important;
38 | }
39 |
40 | .el-menu-item-group__title {
41 | display: none;
42 | }
43 |
44 | .el-menu-item {
45 | &.is-active {
46 | background-color: white;
47 |
48 | i {
49 | color: map.get($base-colors, primary) !important;
50 | }
51 | }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/assets/styles/global/components/iconfont.scss:
--------------------------------------------------------------------------------
1 | @font-face {
2 | font-family: "c-iconfont"; /* Project id 3325521 */
3 | src: url("//at.alicdn.com/t/c/font_3325521_drrl7vh19ye.woff2?t=1664148146521")
4 | format("woff2"),
5 | url("//at.alicdn.com/t/c/font_3325521_drrl7vh19ye.woff?t=1664148146521")
6 | format("woff"),
7 | url("//at.alicdn.com/t/c/font_3325521_drrl7vh19ye.ttf?t=1664148146521")
8 | format("truetype");
9 | }
10 |
11 | .c-iconfont {
12 | font-family: "c-iconfont" !important;
13 | font-style: normal;
14 | -webkit-font-smoothing: antialiased;
15 | -moz-osx-font-smoothing: grayscale;
16 | }
17 |
18 | .c-iconfont--job:before {
19 | content: "\e647";
20 | }
21 |
22 | .c-iconfont--other:before {
23 | content: "\e629";
24 | }
25 |
26 | .c-iconfont--information:before {
27 | content: "\e8ea";
28 | }
29 |
30 | .c-iconfont--talent:before {
31 | content: "\e60d";
32 | }
33 |
34 | .c-iconfont--prize:before {
35 | content: "\e671";
36 | }
37 |
38 | .c-iconfont--research:before {
39 | content: "\e677";
40 | }
41 |
42 | .c-iconfont--introduction:before {
43 | content: "\e685";
44 | }
45 |
46 | .c-iconfont--team:before {
47 | content: "\e62d";
48 | }
49 |
--------------------------------------------------------------------------------
/src/views/login/components/form/script.js:
--------------------------------------------------------------------------------
1 | import { reactive, ref } from "vue";
2 | import { useStore } from "vuex";
3 | import { useRouter } from "vue-router";
4 | import { ElMessage } from "element-plus";
5 | import { useValidators } from "vue-validation";
6 | import { UserFilled } from "@element-plus/icons-vue";
7 |
8 | export default {
9 | setup() {
10 | const { dispatch } = useStore();
11 | const router = useRouter();
12 | const { isRequired } = useValidators();
13 |
14 | const formRef = ref(null);
15 |
16 | const cForm = reactive({
17 | model: {},
18 | rules: {
19 | username: [isRequired({ label: "用户名" })],
20 | password: [isRequired({ label: "密码" })],
21 | },
22 | });
23 |
24 | const submit = () => {
25 | formRef.value.validate(async (valid) => {
26 | if (valid) {
27 | await dispatch("auth/login", cForm.model);
28 | ElMessage.success("登录成功");
29 | await dispatch("auth/getMenus");
30 | await router.push("/");
31 | }
32 | });
33 | };
34 |
35 | return {
36 | UserFilled,
37 | formRef,
38 | cForm,
39 | submit,
40 | };
41 | },
42 | };
43 |
--------------------------------------------------------------------------------
/npm/element-plus-admin/components/list/script.js:
--------------------------------------------------------------------------------
1 | import { useConsts } from "@/composables/use-consts";
2 | import { ref, watch } from "vue";
3 |
4 | export default {
5 | name: "List",
6 | emits: ["page-change", "page-size-change"],
7 | props: {
8 | total: {
9 | type: Number,
10 | default: 0,
11 | },
12 | pageSize: {
13 | type: Number,
14 | default: useConsts().PageSize,
15 | },
16 | currentPage: {
17 | type: Number,
18 | default: 1,
19 | },
20 | showHeader: {
21 | type: Boolean,
22 | default: true,
23 | },
24 | showPagination: {
25 | type: Boolean,
26 | default: true,
27 | },
28 | simpleHeader: {
29 | type: Boolean,
30 | default: true,
31 | },
32 | showPageSizes: {
33 | type: Boolean,
34 | default: false,
35 | },
36 | },
37 | setup(props) {
38 | const currentPageSize = ref(props.pageSize);
39 |
40 | watch(
41 | () => props.pageSize,
42 | (newVal) => {
43 | currentPageSize.value = newVal;
44 | },
45 | { deep: true, immediate: true }
46 | );
47 |
48 | return {
49 | currentPageSize,
50 | };
51 | },
52 | };
53 |
--------------------------------------------------------------------------------
/npm/element-plus-admin/store/modules/auth.js:
--------------------------------------------------------------------------------
1 | import helpers from "jt-helpers";
2 | import { publicManagersApi } from "../../apis/public/managers";
3 |
4 | const state = {
5 | user: {
6 | id: "",
7 | name: "",
8 | token: "",
9 | },
10 | menus: [],
11 | };
12 |
13 | const types = helpers.keyMirror({
14 | SetUser: null,
15 | SetMenus: null,
16 | });
17 |
18 | const mutations = {
19 | [types.SetUser](state, user) {
20 | state.user = user;
21 | },
22 | [types.SetMenus](state, menus) {
23 | state.menus = menus;
24 | },
25 | };
26 |
27 | const actions = {
28 | async login({ commit }, data) {
29 | const res = await publicManagersApi.post({ action: "login", body: data });
30 |
31 | const {
32 | manager: { id, username },
33 | token,
34 | } = res;
35 |
36 | commit(types.SetUser, { id, name: username, token });
37 |
38 | return res;
39 | },
40 |
41 | async getMenus() {
42 | return null;
43 | },
44 | logout({ commit }) {
45 | commit(types.SetUser, state.user);
46 | commit(types.SetMenus, state.menus);
47 | return null;
48 | },
49 | };
50 |
51 | export const auth = {
52 | namespaced: true,
53 | state,
54 | mutations,
55 | actions,
56 | };
57 |
--------------------------------------------------------------------------------
/src/views/products/list/script.js:
--------------------------------------------------------------------------------
1 | import { useList } from "element-plus-admin/components/list/composables/use-list";
2 | import { useEnums } from "element-plus-admin/composables/use-enums";
3 | import { ref } from "vue";
4 | import BForm from "./components/form/index.vue";
5 | import { productsApi } from "@/apis/admin/products";
6 | import { categoriesApi } from "@/apis/admin/categories";
7 |
8 | export default {
9 | components: {
10 | BForm,
11 | },
12 | setup() {
13 | const formRef = ref(null);
14 |
15 | const { enums } = useEnums();
16 |
17 | const { list, currentPage, cFilters, reRender, onPageChange, search, del } =
18 | useList({
19 | api: productsApi,
20 | filters: {
21 | model: {
22 | categoryId: undefined,
23 | name: { $like: "" },
24 | cnName: { $like: "" },
25 | },
26 | rules: {},
27 | },
28 | extraQuery: {
29 | include: [{ model: "Category", as: "category" }],
30 | },
31 | });
32 |
33 | return {
34 | categoriesApi,
35 | formRef,
36 | enums,
37 | list,
38 | currentPage,
39 | cFilters,
40 | reRender,
41 | onPageChange,
42 | search,
43 | del,
44 | };
45 | },
46 | };
47 |
--------------------------------------------------------------------------------
/npm/element-plus-admin/components/layout/sidebar/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
36 |
37 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/npm/element-plus-admin/components/resource-select/script.js:
--------------------------------------------------------------------------------
1 | import { onMounted, ref } from "vue";
2 |
3 | export default {
4 | props: {
5 | placeholder: {
6 | type: String,
7 | default: "请选择",
8 | },
9 | multiple: {
10 | type: Boolean,
11 | default: false,
12 | },
13 | clearable: {
14 | type: Boolean,
15 | default: false,
16 | },
17 | filterable: {
18 | type: Boolean,
19 | default: false,
20 | },
21 | optionLabel: {
22 | type: [String, Function],
23 | default: "name",
24 | },
25 | api: {
26 | type: [Object, Function],
27 | default: () => null,
28 | },
29 | value: {
30 | type: [String, Number],
31 | },
32 | },
33 | emits: ["update:value"],
34 | setup(props, context) {
35 | const list = ref({
36 | items: [],
37 | });
38 |
39 | const render = async () => {
40 | list.value =
41 | typeof props.api === "function"
42 | ? await props.api()
43 | : await props.api.get({});
44 | };
45 |
46 | onMounted(async () => {
47 | await render();
48 | });
49 |
50 | const onChange = (index) => {
51 | context.emit("update:value", index);
52 | };
53 |
54 | return {
55 | list,
56 | render,
57 | onChange,
58 | };
59 | },
60 | };
61 |
--------------------------------------------------------------------------------
/npm/element-plus-admin/components/items/script.js:
--------------------------------------------------------------------------------
1 | import { computed, watch } from "vue";
2 | import { useStore } from "vuex";
3 |
4 | export default {
5 | props: {
6 | ids: {
7 | type: Array,
8 | default: () => [],
9 | },
10 | className: {
11 | type: String,
12 | default: "",
13 | },
14 | labelKey: {
15 | type: String,
16 | default: "name",
17 | },
18 | resource: String,
19 | api: Object,
20 | plain: {
21 | type: Boolean,
22 | default: false,
23 | },
24 | joinString: {
25 | type: String,
26 | default: "、",
27 | },
28 | },
29 | setup(props) {
30 | const { state, dispatch } = useStore();
31 |
32 | const items = computed(() =>
33 | state.items.data[props.resource]
34 | ? state.items.data[props.resource][props.ids.join(",")]
35 | : {}
36 | );
37 |
38 | watch(
39 | () => props.ids,
40 | async (newVal) => {
41 | if (newVal && newVal.length) {
42 | await dispatch("items/getItems", {
43 | resource: props.resource,
44 | api: props.api,
45 | ids: newVal,
46 | });
47 | } else {
48 | items.value = [];
49 | }
50 | },
51 | { immediate: true, deep: true }
52 | );
53 |
54 | return {
55 | items,
56 | };
57 | },
58 | };
59 |
--------------------------------------------------------------------------------
/src/components/layout/utils/get-menus.js:
--------------------------------------------------------------------------------
1 | export const getMenus = () => {
2 | return [
3 | {
4 | name: "product",
5 | title: "商品管理",
6 | icon: "product-filled",
7 | children: [
8 | {
9 | title: "商品列表",
10 | path: "/product/products",
11 | },
12 | {
13 | title: "商品分类",
14 | path: "/product/categories",
15 | },
16 | ],
17 | },
18 | {
19 | name: "ad",
20 | title: "广告管理",
21 | icon: "ad-filled",
22 | children: [
23 | {
24 | title: "广告列表",
25 | path: "/ad/ads",
26 | },
27 | ],
28 | },
29 | {
30 | name: "order",
31 | title: "订单管理",
32 | icon: "order-filled",
33 | children: [
34 | {
35 | title: "订单列表",
36 | path: "/order/orders",
37 | },
38 | ],
39 | },
40 | {
41 | name: "user",
42 | title: "用户管理",
43 | icon: "user-filled",
44 | children: [
45 | {
46 | title: "用户列表",
47 | path: "/user/users",
48 | },
49 | ],
50 | },
51 | {
52 | name: "app",
53 | title: "应用管理",
54 | icon: "app-filled",
55 | children: [
56 | {
57 | title: "应用升级",
58 | path: "/app/app-upgrades",
59 | },
60 | ],
61 | },
62 | ];
63 | };
64 |
--------------------------------------------------------------------------------
/npm/element-plus-admin/components/layout/main/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | {{ menus[0].title }}
9 |
10 | {{ menus[1].title }}
11 |
12 |
13 | {{ $route.query.$menu1Title }}
14 |
15 |
19 | {{ menus[2].title }}
20 |
21 |
22 | {{ $route.query.$menu2Title }}
23 |
24 |
25 | {{ menus[3].title }}
26 |
27 |
28 |
29 | 首页
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/npm/element-plus-admin/components/editor/composables/use-upload.js:
--------------------------------------------------------------------------------
1 | import { useCos } from "../../upload/composables/use-cos";
2 | import { useConsts } from "@/composables/use-consts";
3 | import { ElLoading, ElMessage } from "element-plus";
4 |
5 | export const useUpload = ({ props }) => {
6 | const { ApiUrl } = useConsts();
7 |
8 | const configEditor = async ({ editorConfig }) => {
9 | if (props.cosConfig) {
10 | const cos = useCos(props.cosConfig);
11 |
12 | const customUpload = async (file, insertFn) => {
13 | const loading = ElLoading.service({
14 | text: "正在上传...",
15 | lock: true,
16 | background: "rgba(122, 122, 122, 0.1)",
17 | });
18 |
19 | await cos.initialize();
20 |
21 | const { id } = await cos.upload(file);
22 | insertFn(`${ApiUrl}/public/files/${id}`);
23 |
24 | loading.close();
25 | ElMessage.success("上传成功");
26 | };
27 |
28 | editorConfig.MENU_CONF["uploadImage"] = { customUpload };
29 | editorConfig.MENU_CONF["uploadVideo"] = { customUpload };
30 | } else {
31 | const config = {
32 | server: props.uploadAction,
33 | fieldName: "file",
34 | maxNumberOfFiles: 1,
35 | headers: props.uploadHeaders,
36 | customInsert(res, insertFn) {
37 | insertFn(`${ApiUrl}/public/files/${res.data.id}`);
38 | },
39 | };
40 |
41 | editorConfig.MENU_CONF["uploadImage"] = config;
42 | editorConfig.MENU_CONF["uploadVideo"] = config;
43 | }
44 | };
45 |
46 | return {
47 | configEditor,
48 | };
49 | };
50 |
--------------------------------------------------------------------------------
/npm/element-plus-admin/components/upload/files/script.js:
--------------------------------------------------------------------------------
1 | import { computed, ref, watch } from "vue";
2 | import { useHelpers } from "@/composables/use-helpers";
3 | import { CircleCheck, Close, Document } from "@element-plus/icons-vue";
4 | import FileViewer from "../../file-viewer/index.vue";
5 | import { publicFilesApi } from "../../../apis/public/files";
6 | import { useStore } from "vuex";
7 |
8 | export default {
9 | components: {
10 | "el-icon-document": Document,
11 | "el-circle-check": CircleCheck,
12 | "el-close": Close,
13 | "c-file-viewer": FileViewer,
14 | },
15 | props: {
16 | ids: {
17 | type: Array,
18 | default: () => [],
19 | },
20 | officeViewerServiceUrl: String,
21 | },
22 | setup(props) {
23 | const { state, dispatch } = useStore();
24 |
25 | const { getFileUrl } = useHelpers();
26 |
27 | const fileViewer = ref(null);
28 |
29 | const files = computed(() => {
30 | return state.items.data.files && props.ids
31 | ? state.items.data.files[props.ids.join(",")] || []
32 | : [];
33 | });
34 |
35 | watch(
36 | () => props.ids,
37 | async (newVal) => {
38 | if (newVal && newVal.length) {
39 | await dispatch("items/getItems", {
40 | resource: "files",
41 | api: publicFilesApi,
42 | ids: newVal,
43 | });
44 | } else {
45 | files.value = [];
46 | }
47 | },
48 | { immediate: true, deep: true }
49 | );
50 |
51 | return {
52 | fileViewer,
53 | files,
54 | getFileUrl,
55 | };
56 | },
57 | };
58 |
--------------------------------------------------------------------------------
/npm/element-plus-admin/components/file-viewer/script.js:
--------------------------------------------------------------------------------
1 | import { ElMessage } from "element-plus";
2 | import { useConsts } from "@/composables/use-consts";
3 | import { reactive, ref } from "vue";
4 | import OfficeViewer from "./components/office-viewer/index.vue";
5 |
6 | export default {
7 | components: {
8 | "c-office-viewer": OfficeViewer,
9 | },
10 | props: {
11 | urls: {
12 | type: Array,
13 | default: () => [],
14 | },
15 | officeViewerServiceUrl: String,
16 | },
17 | setup() {
18 | const officeViewer = ref(null);
19 |
20 | const cImageViewer = reactive({
21 | visible: false,
22 | index: -1,
23 | });
24 |
25 | const preview = (file, index) => {
26 | switch (true) {
27 | case ["jpg", "jpeg", "png", "gif"].includes(file.ext):
28 | previewImage(index);
29 | break;
30 |
31 | case ["ppt", "pptx", "doc", "docx", "xls", "xlsx"].includes(file.ext):
32 | previewOffice(file);
33 | break;
34 |
35 | default:
36 | ElMessage.error("该文件类型暂不支持预览");
37 | break;
38 | }
39 | };
40 |
41 | const previewImage = (index) => {
42 | cImageViewer.index = index;
43 | cImageViewer.visible = true;
44 | };
45 |
46 | const previewOffice = ({ dir, date, uuid, ext }) => {
47 | const url = `${useConsts().StaticUrl}/${
48 | dir ? `${dir}/` : ""
49 | }${date}/${uuid}.${ext}`;
50 |
51 | officeViewer.value.show({ src: url });
52 | };
53 |
54 | return {
55 | officeViewer,
56 | cImageViewer,
57 | preview,
58 | };
59 | },
60 | };
61 |
--------------------------------------------------------------------------------
/src/views/ads/list/components/form/script.js:
--------------------------------------------------------------------------------
1 | import { reactive, ref } from "vue";
2 | import { useHelpers } from "@/composables/use-helpers";
3 | import { adsApi } from "@/apis/admin/ads";
4 | import { useEnums } from "element-plus-admin/composables/use-enums";
5 | import { useValidators } from "vue-validation";
6 | import { useFormDialog } from "element-plus-admin/composables/use-form-dialog";
7 | import { tencentCloudCosApi } from "@/apis/admin/tencent-cloud-cos";
8 | import { filesApi } from "@/apis/admin/files";
9 | import { UploadTo } from "element-plus-admin/enums/upload-to";
10 |
11 | export default {
12 | emits: ["ok"],
13 | setup(props, context) {
14 | const formRef = ref(null);
15 |
16 | const { deepCopy } = useHelpers();
17 |
18 | const { isRequired } = useValidators();
19 |
20 | const { enums } = useEnums();
21 |
22 | const cDialog = reactive({
23 | visible: false,
24 | });
25 |
26 | const initialModel = {};
27 |
28 | const cForm = reactive({
29 | id: 0,
30 | model: deepCopy(initialModel),
31 | rules: {
32 | imageFileId: [isRequired({ message: "请选择广告图片" })],
33 | },
34 | });
35 |
36 | const { show, validateField, submit } = useFormDialog({
37 | api: adsApi,
38 | cDialog,
39 | cForm,
40 | formRef,
41 | initialModel,
42 | onOk() {
43 | context.emit("ok");
44 | },
45 | });
46 |
47 | return {
48 | tencentCloudCosApi,
49 | filesApi,
50 | UploadTo,
51 | enums,
52 | formRef,
53 | cDialog,
54 | cForm,
55 | show,
56 | validateField,
57 | submit,
58 | };
59 | },
60 | };
61 |
--------------------------------------------------------------------------------
/npm/element-plus-admin/composables/use-form-dialog.js:
--------------------------------------------------------------------------------
1 | import { watch } from "vue";
2 | import { deepCopy } from "jt-helpers";
3 | import { ElMessage } from "element-plus";
4 |
5 | export const useFormDialog = ({
6 | api,
7 | cDialog,
8 | cForm,
9 | formRef,
10 | initialModel,
11 | onSubmit,
12 | onOk,
13 | } = {}) => {
14 | watch(
15 | () => cDialog.visible,
16 | (newVal) => {
17 | if (!newVal) {
18 | cForm.model = deepCopy(initialModel);
19 | formRef.value && formRef.value.resetFields();
20 | }
21 | }
22 | );
23 |
24 | const show = ({ id, ...rest } = {}) => {
25 | cForm.id = id || 0;
26 | cForm.model = { ...initialModel, ...rest };
27 |
28 | cDialog.visible = true;
29 | formRef.value && formRef.value.resetFields();
30 | };
31 |
32 | const validateField = (field) => {
33 | formRef.value.validateField(field);
34 | };
35 |
36 | const validate = () => {
37 | return new Promise((resolve, reject) => {
38 | formRef.value.validate(async (valid) => {
39 | if (valid) {
40 | resolve(cForm);
41 | } else {
42 | reject();
43 | }
44 | });
45 | });
46 | };
47 |
48 | const submit = async () => {
49 | const { id, model } = await validate();
50 |
51 | if (onSubmit) {
52 | await onSubmit({ id, model });
53 | } else {
54 | await api[id ? "put" : "post"]({ id, body: model });
55 | ElMessage.success(id ? "修改成功" : "新增成功");
56 | }
57 |
58 | cDialog.visible = false;
59 | onOk && onOk();
60 | };
61 |
62 | return {
63 | show,
64 | validate,
65 | validateField,
66 | submit,
67 | };
68 | };
69 |
--------------------------------------------------------------------------------
/src/views/jobs/list/components/form/script.js:
--------------------------------------------------------------------------------
1 | import { reactive, ref } from "vue";
2 | import { useHelpers } from "@/composables/use-helpers";
3 | import { jobsApi } from "@/apis/admin/jobs";
4 | import { useEnums } from "element-plus-admin/composables/use-enums";
5 | import { useValidators } from "vue-validation";
6 | import { useFormDialog } from "element-plus-admin/composables/use-form-dialog";
7 | import { tencentCloudCosApi } from "@/apis/admin/tencent-cloud-cos";
8 | import { filesApi } from "@/apis/admin/files";
9 | import { UploadTo } from "element-plus-admin/enums/upload-to";
10 |
11 | export default {
12 | emits: ["ok"],
13 | setup(props, context) {
14 | const formRef = ref(null);
15 |
16 | const { deepCopy } = useHelpers();
17 |
18 | const { isRequired } = useValidators();
19 |
20 | const { enums } = useEnums();
21 |
22 | const cDialog = reactive({
23 | visible: false,
24 | });
25 |
26 | const initialModel = {};
27 |
28 | const cForm = reactive({
29 | id: 0,
30 | model: deepCopy(initialModel),
31 | rules: {
32 | imageFileId: [isRequired({ message: "请选择广告图片" })],
33 | },
34 | });
35 |
36 | const { show, validateField, submit } = useFormDialog({
37 | api: jobsApi,
38 | cDialog,
39 | cForm,
40 | formRef,
41 | initialModel,
42 | onOk() {
43 | context.emit("ok");
44 | },
45 | });
46 |
47 | return {
48 | tencentCloudCosApi,
49 | filesApi,
50 | UploadTo,
51 | enums,
52 | formRef,
53 | cDialog,
54 | cForm,
55 | show,
56 | validateField,
57 | submit,
58 | };
59 | },
60 | };
61 |
--------------------------------------------------------------------------------
/src/assets/styles/global/components/iconfont-20230706.scss:
--------------------------------------------------------------------------------
1 | @font-face {
2 | font-family: "c-iconfont"; /* Project id 2745224 */
3 | src: url("//at.alicdn.com/t/font_2745224_q7cc1l2clf.woff2?t=1648688085392")
4 | format("woff2"),
5 | url("//at.alicdn.com/t/font_2745224_q7cc1l2clf.woff?t=1648688085392")
6 | format("woff"),
7 | url("//at.alicdn.com/t/font_2745224_q7cc1l2clf.ttf?t=1648688085392")
8 | format("truetype");
9 | }
10 |
11 | .c-iconfont {
12 | font-family: "c-iconfont" !important;
13 | font-style: normal;
14 | -webkit-font-smoothing: antialiased;
15 | -moz-osx-font-smoothing: grayscale;
16 | }
17 |
18 | .c-iconfont--home-filled:before {
19 | content: "\e8c6";
20 | }
21 |
22 | .c-iconfont--app-filled:before {
23 | content: "\e6d4";
24 | }
25 |
26 | .c-iconfont--product-filled:before {
27 | content: "\e888";
28 | }
29 |
30 | .c-iconfont--ad-filled:before {
31 | content: "\e600";
32 | }
33 |
34 | .c-iconfont--order-filled:before {
35 | content: "\e616";
36 | }
37 |
38 | .c-iconfont--camera-filled:before {
39 | content: "\e60d";
40 | }
41 |
42 | .c-iconfont--upload-filled:before {
43 | content: "\e613";
44 | }
45 |
46 | .c-iconfont--edit-filled:before {
47 | content: "\e607";
48 | }
49 |
50 | .c-iconfont--more-drop-filled:before {
51 | content: "\e602";
52 | }
53 |
54 | .c-iconfont--server-filled:before {
55 | content: "\e65b";
56 | }
57 |
58 | .c-iconfont--setup-filled:before {
59 | content: "\e611";
60 | }
61 |
62 | .c-iconfont--safe-filled:before {
63 | content: "\e63e";
64 | }
65 |
66 | .c-iconfont--lock-filled:before {
67 | content: "\e62e";
68 | }
69 |
70 | .c-iconfont--user-filled:before {
71 | content: "\e601";
72 | }
73 |
--------------------------------------------------------------------------------
/npm/element-plus-admin/components/file-list/script.js:
--------------------------------------------------------------------------------
1 | import { ref, watch } from "vue";
2 | import { useHelpers } from "@/composables/use-helpers";
3 | import FileViewer from "../file-viewer/index.vue";
4 | import { publicFilesApi } from "../../apis/public/files";
5 |
6 | export default {
7 | components: {
8 | "c-file-viewer": FileViewer,
9 | },
10 | props: {
11 | ids: {
12 | type: Array,
13 | default: () => [],
14 | },
15 | className: {
16 | type: String,
17 | default: "",
18 | },
19 | canPreview: {
20 | type: Boolean,
21 | default: true,
22 | },
23 | officeViewerServiceUrl: String,
24 | },
25 | setup(props) {
26 | const { getFileUrl } = useHelpers();
27 |
28 | const fileViewer = ref(null);
29 |
30 | const files = ref([]);
31 |
32 | watch(
33 | () => props.ids,
34 | async (newVal) => {
35 | if (newVal && newVal.length) {
36 | const { items } = await publicFilesApi.post({
37 | action: "getAllByIds",
38 | body: { ids: newVal },
39 | });
40 |
41 | files.value = items.map(({ id, name, ext }) => ({
42 | id,
43 | name,
44 | ext,
45 | url: getFileUrl({ id }),
46 | }));
47 | } else {
48 | files.value = [];
49 | }
50 | },
51 | { immediate: true, deep: true }
52 | );
53 |
54 | const preview = (file, index) => {
55 | if (!props.canPreview) return;
56 | fileViewer.value.preview(file, index);
57 | };
58 |
59 | return {
60 | fileViewer,
61 | files,
62 | getFileUrl,
63 | preview,
64 | };
65 | },
66 | };
67 |
--------------------------------------------------------------------------------
/src/views/login/components/form/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
54 |
55 |
56 |
57 |
--------------------------------------------------------------------------------
/npm/element-plus-admin/components/layout/sidebar/script.js:
--------------------------------------------------------------------------------
1 | import { ref } from "vue";
2 | import { onBeforeRouteUpdate, useRoute, useRouter } from "vue-router";
3 |
4 | export default {
5 | props: {
6 | showHomeMenu: {
7 | type: Boolean,
8 | default: false,
9 | },
10 | menus: {
11 | type: Array,
12 | default: () => [],
13 | },
14 | },
15 | setup(props) {
16 | const router = useRouter();
17 | const route = useRoute();
18 |
19 | const getActiveKey = (path) => {
20 | let key = "-1";
21 |
22 | props.menus.forEach((item1, index1) => {
23 | item1.children.forEach((item2, index2) => {
24 | const routePaths = (path || route.path).split("/");
25 | const itemPaths = item2.path.split("/");
26 |
27 | if (
28 | routePaths[1] === itemPaths[1] &&
29 | routePaths[2] === itemPaths[2]
30 | ) {
31 | key = `${index1}-${index2}`;
32 | }
33 | });
34 | });
35 |
36 | return key;
37 | };
38 |
39 | const activeKey = ref(getActiveKey());
40 |
41 | onBeforeRouteUpdate((to, from, next) => {
42 | activeKey.value = getActiveKey(to.path);
43 | next();
44 | });
45 |
46 | const onSelect = async (key) => {
47 | if (key === "-1") {
48 | await router.push("/");
49 | } else {
50 | const indexes = key.split("-");
51 | const index1 = indexes[0];
52 | const index2 = indexes[1];
53 |
54 | const { path } = props.menus[index1].children[index2];
55 |
56 | await router.push(path);
57 | }
58 | };
59 |
60 | return {
61 | router,
62 | activeKey,
63 | onSelect,
64 | };
65 | },
66 | };
67 |
--------------------------------------------------------------------------------
/src/views/categories/list/components/form/script.js:
--------------------------------------------------------------------------------
1 | import { reactive, ref } from "vue";
2 | import { useHelpers } from "@/composables/use-helpers";
3 | import { useEnums } from "element-plus-admin/composables/use-enums";
4 | import { useValidators } from "vue-validation";
5 | import { useFormDialog } from "element-plus-admin/composables/use-form-dialog";
6 | import { tencentCloudCosApi } from "@/apis/admin/tencent-cloud-cos";
7 | import { filesApi } from "@/apis/admin/files";
8 | import { UploadTo } from "element-plus-admin/enums/upload-to";
9 | import { categoriesApi } from "@/apis/admin/categories";
10 |
11 | export default {
12 | emits: ["ok"],
13 | setup(props, context) {
14 | const formRef = ref(null);
15 |
16 | const { deepCopy } = useHelpers();
17 |
18 | const { isRequired } = useValidators();
19 |
20 | const { enums } = useEnums();
21 |
22 | const cDialog = reactive({
23 | visible: false,
24 | });
25 |
26 | const initialModel = {};
27 |
28 | const cForm = reactive({
29 | id: 0,
30 | model: deepCopy(initialModel),
31 | rules: {
32 | name: [isRequired({ label: "商品分类名称" })],
33 | iconFileId: [isRequired({ message: "请选择商品分类图标" })],
34 | },
35 | });
36 |
37 | const { show, validateField, submit } = useFormDialog({
38 | api: categoriesApi,
39 | cDialog,
40 | cForm,
41 | formRef,
42 | initialModel,
43 | onOk() {
44 | context.emit("ok");
45 | },
46 | });
47 |
48 | return {
49 | tencentCloudCosApi,
50 | filesApi,
51 | UploadTo,
52 | enums,
53 | formRef,
54 | cDialog,
55 | cForm,
56 | show,
57 | validateField,
58 | submit,
59 | };
60 | },
61 | };
62 |
--------------------------------------------------------------------------------
/src/views/researches/list/components/form/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
7 |
14 |
15 |
16 |
17 |
18 |
30 |
31 |
32 |
44 |
45 |
46 |
47 | 取消
48 | 确定
49 |
50 |
51 |
52 |
53 |
54 |
--------------------------------------------------------------------------------
/src/views/users/list/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 | 刷新
9 |
10 |
11 |
17 |
18 |
23 |
24 |
25 |
30 |
31 |
32 | 查询
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 | {{ $helpers.getItem(enums.Gender, "value", row.gender)["label"] }}
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "my-app",
3 | "version": "0.0.0",
4 | "scripts": {
5 | "serve": "vue-cli-service serve",
6 | "build": "vue-cli-service build",
7 | "build:test": "vue-cli-service build --mode test",
8 | "lint": "vue-cli-service lint",
9 | "format": "prettier --write \"src/**/*.{vue,js}\""
10 | },
11 | "dependencies": {
12 | "@element-plus/icons-vue": "^2.1.0",
13 | "element-plus": "^2.3.7",
14 | "element-plus-admin": "^1.2.17",
15 | "jt-helpers": "^1.0.12",
16 | "jt-storage": "^0.0.2",
17 | "jt-time": "^0.0.10",
18 | "pinia": "^2.1.4",
19 | "pinia-plugin-persistedstate": "^3.1.0",
20 | "sass-utils": "^1.1.12",
21 | "vue": "^3.3.4",
22 | "vue-router": "^4.2.2",
23 | "vue-validation": "^1.0.9",
24 | "vuex": "^4.1.0",
25 | "vuex-persistedstate": "^4.1.0"
26 | },
27 | "devDependencies": {
28 | "@babel/core": "^7.22.5",
29 | "@babel/eslint-parser": "^7.22.5",
30 | "@vue/cli-plugin-babel": "^5.0.8",
31 | "@vue/cli-plugin-eslint": "^5.0.8",
32 | "@vue/cli-service": "^5.0.8",
33 | "eslint": "^8.43.0",
34 | "eslint-config-prettier": "^8.8.0",
35 | "eslint-plugin-prettier": "^4.2.1",
36 | "eslint-plugin-vue": "^9.15.0",
37 | "prettier": "^2.8.8",
38 | "sass": "^1.63.6",
39 | "sass-loader": "^13.3.2"
40 | },
41 | "eslintConfig": {
42 | "root": true,
43 | "env": {
44 | "node": true
45 | },
46 | "extends": [
47 | "plugin:vue/vue3-essential",
48 | "eslint:recommended",
49 | "plugin:prettier/recommended"
50 | ],
51 | "parserOptions": {
52 | "parser": "@babel/eslint-parser"
53 | },
54 | "rules": {
55 | "vue/multi-word-component-names": 0
56 | }
57 | },
58 | "browserslist": [
59 | "> 1%",
60 | "last 2 versions",
61 | "not dead",
62 | "not ie 11"
63 | ]
64 | }
65 |
--------------------------------------------------------------------------------
/src/views/products/list/components/form/script.js:
--------------------------------------------------------------------------------
1 | import { reactive, ref } from "vue";
2 | import { useHelpers } from "@/composables/use-helpers";
3 | import { useEnums } from "element-plus-admin/composables/use-enums";
4 | import { useValidators } from "vue-validation";
5 | import { useFormDialog } from "element-plus-admin/composables/use-form-dialog";
6 | import { tencentCloudCosApi } from "@/apis/admin/tencent-cloud-cos";
7 | import { filesApi } from "@/apis/admin/files";
8 | import { categoriesApi } from "@/apis/admin/categories";
9 | import { UploadTo } from "element-plus-admin/enums/upload-to";
10 | import { productsApi } from "@/apis/admin/products";
11 |
12 | export default {
13 | emits: ["ok"],
14 | setup(props, context) {
15 | const formRef = ref(null);
16 |
17 | const { deepCopy } = useHelpers();
18 |
19 | const { isRequired } = useValidators();
20 |
21 | const { enums } = useEnums();
22 |
23 | const cDialog = reactive({
24 | visible: false,
25 | });
26 |
27 | const initialModel = {};
28 |
29 | const cForm = reactive({
30 | id: 0,
31 | model: deepCopy(initialModel),
32 | rules: {
33 | categoryId: [isRequired({ label: "商品分类" })],
34 | name: [isRequired({ label: "商品名称" })],
35 | imageFileIds: [isRequired({ message: "请选择商品图片" })],
36 | },
37 | });
38 |
39 | const { show, validateField, submit } = useFormDialog({
40 | api: productsApi,
41 | cDialog,
42 | cForm,
43 | formRef,
44 | initialModel,
45 | onOk() {
46 | context.emit("ok");
47 | },
48 | });
49 |
50 | return {
51 | tencentCloudCosApi,
52 | filesApi,
53 | categoriesApi,
54 | UploadTo,
55 | enums,
56 | formRef,
57 | cDialog,
58 | cForm,
59 | show,
60 | validateField,
61 | submit,
62 | };
63 | },
64 | };
65 |
--------------------------------------------------------------------------------
/src/views/app-upgrades/list/components/form/script.js:
--------------------------------------------------------------------------------
1 | import { reactive, ref } from "vue";
2 | import { useHelpers } from "@/composables/use-helpers";
3 | import { useEnums } from "element-plus-admin/composables/use-enums";
4 | import { useValidators } from "vue-validation";
5 | import { useFormDialog } from "element-plus-admin/composables/use-form-dialog";
6 | import { tencentCloudCosApi } from "@/apis/admin/tencent-cloud-cos";
7 | import { filesApi } from "@/apis/admin/files";
8 | import { UploadTo } from "element-plus-admin/enums/upload-to";
9 | import { appUpgradesApi } from "@/apis/admin/app-upgrades";
10 |
11 | export default {
12 | emits: ["ok"],
13 | setup(props, context) {
14 | const formRef = ref(null);
15 |
16 | const { deepCopy } = useHelpers();
17 |
18 | const { isRequired } = useValidators();
19 |
20 | const { enums } = useEnums();
21 |
22 | const cDialog = reactive({
23 | visible: false,
24 | });
25 |
26 | const initialModel = {
27 | status: 1,
28 | };
29 |
30 | const cForm = reactive({
31 | id: 0,
32 | model: deepCopy(initialModel),
33 | rules: {
34 | platform: [isRequired({ label: "平台" })],
35 | versionName: [isRequired({ label: "版本名称" })],
36 | versionCode: [isRequired({ label: "版本号" })],
37 | log: [isRequired({ label: "更新日志" })],
38 | },
39 | });
40 |
41 | const { show, validateField, submit } = useFormDialog({
42 | api: appUpgradesApi,
43 | cDialog,
44 | cForm,
45 | formRef,
46 | initialModel,
47 | onOk() {
48 | context.emit("ok");
49 | },
50 | });
51 |
52 | return {
53 | tencentCloudCosApi,
54 | filesApi,
55 | UploadTo,
56 | enums,
57 | formRef,
58 | cDialog,
59 | cForm,
60 | show,
61 | validateField,
62 | submit,
63 | };
64 | },
65 | };
66 |
--------------------------------------------------------------------------------
/src/views/articles/list/components/form/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
8 |
15 |
16 |
17 |
18 |
19 |
31 |
32 |
33 |
45 |
46 |
47 |
48 | 取消
49 |
50 | 获取草稿
51 |
52 | 保存为草稿
53 | 发布
54 |
55 |
56 |
57 |
58 |
59 |
--------------------------------------------------------------------------------
/src/views/researches/list/components/form/script.js:
--------------------------------------------------------------------------------
1 | import { reactive, ref } from "vue";
2 | import { useHelpers } from "@/composables/use-helpers";
3 | import { researchesApi } from "@/apis/admin/researches";
4 | import { ElMessage } from "element-plus";
5 | import { useValidators } from "vue-validation";
6 | import { useFormDialog } from "element-plus-admin/composables/use-form-dialog";
7 | import { tencentCloudCosApi } from "@/apis/admin/tencent-cloud-cos";
8 | import { filesApi } from "@/apis/admin/files";
9 | import { UploadTo } from "element-plus-admin/enums/upload-to";
10 |
11 | export default {
12 | props: {
13 | path: String,
14 | },
15 | emits: ["ok"],
16 | setup(props, context) {
17 | const formRef = ref(null);
18 |
19 | const { deepCopy } = useHelpers();
20 |
21 | const { isRequired } = useValidators();
22 |
23 | const cDialog = reactive({
24 | visible: false,
25 | });
26 |
27 | const initialModel = {};
28 |
29 | const cForm = reactive({
30 | id: 0,
31 | model: deepCopy(initialModel),
32 | rules: {
33 | title: [isRequired({ label: "标题" })],
34 | content: [isRequired({ label: "内容" })],
35 | },
36 | });
37 |
38 | const { show, validate, validateField } = useFormDialog({
39 | cDialog,
40 | cForm,
41 | formRef,
42 | initialModel,
43 | });
44 |
45 | const submit = async () => {
46 | const { id, model } = await validate();
47 |
48 | await researchesApi[id ? "put" : "post"]({
49 | id,
50 | body: { ...model, path: props.path },
51 | query: {
52 | where: { path: props.path },
53 | },
54 | });
55 | ElMessage.success(id ? "修改成功" : "新增成功");
56 | context.emit("ok");
57 | cDialog.visible = false;
58 | };
59 |
60 | return {
61 | tencentCloudCosApi,
62 | filesApi,
63 | UploadTo,
64 | formRef,
65 | cDialog,
66 | cForm,
67 | show,
68 | validateField,
69 | submit,
70 | };
71 | },
72 | };
73 |
--------------------------------------------------------------------------------
/src/views/researches/list/script.js:
--------------------------------------------------------------------------------
1 | import { useList } from "element-plus-admin/components/list/composables/use-list";
2 | import { useEnums } from "element-plus-admin/composables/use-enums";
3 | import { onMounted, ref } from "vue";
4 | import { researchesApi } from "@/apis/admin/researches";
5 | import Form from "./components/form/index.vue";
6 | import { ElMessage } from "element-plus";
7 | import { useRoute } from "vue-router";
8 |
9 | export default {
10 | components: {
11 | "b-form": Form,
12 | },
13 | setup() {
14 | const { params } = useRoute();
15 |
16 | const formRef = ref(null);
17 |
18 | const currentPath = ref("");
19 |
20 | const { enums } = useEnums();
21 |
22 | const {
23 | list,
24 | currentPage,
25 | cFilters,
26 | reRender,
27 | onPageChange,
28 | search,
29 | initialize,
30 | } = useList({
31 | api: researchesApi,
32 | autoRender: false,
33 | filters: {
34 | model: {
35 | title: { $like: "" },
36 | },
37 | rules: {},
38 | },
39 | extraQuery: () => ({
40 | order: [["order", "ASC"]],
41 | where: { path: currentPath.value, ...cFilters.model },
42 | }),
43 | beforeRouteUpdate(to, from) {
44 | if (to.params.path !== from.params.path) {
45 | currentPath.value = to.params.path.replace("-", "/");
46 | }
47 | },
48 | });
49 |
50 | onMounted(async () => {
51 | currentPath.value = params.path.replace("-", "/");
52 |
53 | await initialize({
54 | filters: {
55 | title: { $like: "" },
56 | },
57 | });
58 | });
59 |
60 | const del = async ({ id }) => {
61 | await researchesApi.delete({ id });
62 | ElMessage.success("删除成功");
63 | await reRender();
64 | };
65 |
66 | return {
67 | researchesApi,
68 | currentPath,
69 | formRef,
70 | enums,
71 | list,
72 | currentPage,
73 | cFilters,
74 | reRender,
75 | onPageChange,
76 | search,
77 | del,
78 | };
79 | },
80 | };
81 |
--------------------------------------------------------------------------------
/src/views/team-members/list/script.js:
--------------------------------------------------------------------------------
1 | import { useList } from "element-plus-admin/components/list/composables/use-list";
2 | import { useEnums } from "element-plus-admin/composables/use-enums";
3 | import { onMounted, ref } from "vue";
4 | import { teamMembersApi } from "@/apis/admin/team-members";
5 | import Form from "./components/form/index.vue";
6 | import { ElMessage } from "element-plus";
7 | import { useRoute } from "vue-router";
8 |
9 | export default {
10 | components: {
11 | "b-form": Form,
12 | },
13 | setup() {
14 | const { params } = useRoute();
15 |
16 | const form = ref(null);
17 |
18 | const currentPath = ref("");
19 |
20 | const { enums } = useEnums();
21 |
22 | const {
23 | list,
24 | currentPage,
25 | cFilters,
26 | reRender,
27 | onPageChange,
28 | search,
29 | initialize,
30 | } = useList({
31 | api: teamMembersApi,
32 | autoRender: false,
33 | filters: {
34 | model: {
35 | name: { $like: "" },
36 | },
37 | rules: {},
38 | },
39 | extraQuery: () => ({
40 | order: [["order", "ASC"]],
41 | where: { path: currentPath.value, ...cFilters.model },
42 | }),
43 | beforeRouteUpdate(to, from) {
44 | if (to.params.path !== from.params.path) {
45 | currentPath.value = to.params.path.replace("-", "/");
46 | }
47 | },
48 | });
49 |
50 | onMounted(async () => {
51 | currentPath.value = params.path.replace("-", "/");
52 |
53 | await initialize({
54 | filters: {
55 | name: { $like: "" },
56 | },
57 | });
58 | });
59 |
60 | const del = async ({ id }) => {
61 | await teamMembersApi.delete({ id });
62 | ElMessage.success("删除成功");
63 | await reRender();
64 | };
65 |
66 | return {
67 | teamMembersApi,
68 | currentPath,
69 | form,
70 | enums,
71 | list,
72 | currentPage,
73 | cFilters,
74 | reRender,
75 | onPageChange,
76 | search,
77 | del,
78 | };
79 | },
80 | };
81 |
--------------------------------------------------------------------------------
/src/views/ads/list/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 | 刷新
9 | 新增
10 |
11 |
12 |
18 |
19 |
24 |
25 |
26 | 查询
27 |
28 |
29 |
30 |
31 |
32 |
33 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 | 编辑
47 |
48 |
49 | 删除
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
--------------------------------------------------------------------------------
/src/views/team-members/list/components/form/script.js:
--------------------------------------------------------------------------------
1 | import { reactive, ref } from "vue";
2 | import { useHelpers } from "@/composables/use-helpers";
3 | import { teamMembersApi } from "@/apis/admin/team-members";
4 | import { ElMessage } from "element-plus";
5 | import { useEnums } from "element-plus-admin/composables/use-enums";
6 | import { useValidators } from "vue-validation";
7 | import { useFormDialog } from "element-plus-admin/composables/use-form-dialog";
8 | import { tencentCloudCosApi } from "@/apis/admin/tencent-cloud-cos";
9 | import { filesApi } from "@/apis/admin/files";
10 | import { UploadTo } from "element-plus-admin/enums/upload-to";
11 |
12 | export default {
13 | props: {
14 | path: String,
15 | },
16 | emits: ["ok"],
17 | setup(props, context) {
18 | const formRef = ref(null);
19 |
20 | const { deepCopy } = useHelpers();
21 |
22 | const { isRequired } = useValidators();
23 |
24 | const { enums } = useEnums();
25 |
26 | const cDialog = reactive({
27 | visible: false,
28 | });
29 |
30 | const initialModel = {};
31 |
32 | const cForm = reactive({
33 | id: 0,
34 | model: deepCopy(initialModel),
35 | rules: {
36 | name: [isRequired({ label: "姓名" })],
37 | },
38 | });
39 |
40 | const { show, validate, validateField } = useFormDialog({
41 | cDialog,
42 | cForm,
43 | formRef,
44 | initialModel,
45 | });
46 |
47 | const submit = async () => {
48 | const { id, model } = await validate();
49 |
50 | await teamMembersApi[id ? "put" : "post"]({
51 | id,
52 | body: { ...model, path: props.path },
53 | query: {
54 | where: { path: props.path },
55 | },
56 | });
57 | ElMessage.success(id ? "修改成功" : "新增成功");
58 | context.emit("ok");
59 | cDialog.visible = false;
60 | };
61 |
62 | return {
63 | tencentCloudCosApi,
64 | filesApi,
65 | UploadTo,
66 | enums,
67 | formRef,
68 | cDialog,
69 | cForm,
70 | show,
71 | validateField,
72 | submit,
73 | };
74 | },
75 | };
76 |
--------------------------------------------------------------------------------
/src/views/categories/list/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 | 刷新
9 | 新增
10 |
11 |
12 |
18 |
19 |
24 |
25 |
26 | 查询
27 |
28 |
29 |
30 |
31 |
32 |
33 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 | 编辑
47 |
48 |
49 | 删除
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
--------------------------------------------------------------------------------
/npm/element-plus-admin/components/editor/script.js:
--------------------------------------------------------------------------------
1 | import "@wangeditor/editor/dist/css/style.css";
2 | import { onBeforeUnmount, ref, shallowRef, watch } from "vue";
3 | import { Editor, Toolbar } from "@wangeditor/editor-for-vue";
4 | import { useConsts } from "@/composables/use-consts";
5 | import { useAuth } from "../../composables/use-auth";
6 | import { useUpload } from "./composables/use-upload";
7 |
8 | const { ApiUrl } = useConsts();
9 | const { getHeaders } = useAuth();
10 |
11 | export default {
12 | components: { Editor, Toolbar },
13 | props: {
14 | value: {
15 | type: String,
16 | default: "",
17 | },
18 | style: {
19 | type: String,
20 | default: "height: 500px",
21 | },
22 | uploadAction: {
23 | type: String,
24 | default: `${ApiUrl}/admin/files/actions/upload`,
25 | },
26 | uploadHeaders: {
27 | type: Object,
28 | default: () => getHeaders(),
29 | },
30 | cosConfig: {
31 | type: Object,
32 | default: () => null,
33 | },
34 | },
35 | emits: ["update:value"],
36 | setup(props, context) {
37 | const upload = useUpload({ props });
38 |
39 | const editorRef = shallowRef();
40 |
41 | const valueHtml = ref(props.value);
42 |
43 | watch(
44 | () => props.value,
45 | (newVal) => {
46 | valueHtml.value = newVal;
47 | }
48 | );
49 |
50 | watch(
51 | () => valueHtml.value,
52 | (newVal) => {
53 | context.emit("update:value", newVal);
54 | }
55 | );
56 |
57 | const toolbarConfig = {};
58 |
59 | const editorConfig = {
60 | MENU_CONF: {},
61 | placeholder: "请输入内容...",
62 | };
63 |
64 | upload.configEditor({ editorConfig });
65 |
66 | onBeforeUnmount(() => {
67 | const editor = editorRef.value;
68 | if (editor == null) return;
69 | editor.destroy();
70 | });
71 |
72 | const onCreated = (editor) => {
73 | editorRef.value = editor;
74 | };
75 |
76 | return {
77 | editorRef,
78 | valueHtml,
79 | toolbarConfig,
80 | editorConfig,
81 | onCreated,
82 | };
83 | },
84 | };
85 |
--------------------------------------------------------------------------------
/src/views/contents/script.js:
--------------------------------------------------------------------------------
1 | import { onMounted, reactive, ref } from "vue";
2 | import { useValidators } from "vue-validation";
3 | import { onBeforeRouteUpdate, useRoute } from "vue-router";
4 | import { contentsApi } from "@/apis/admin/contents";
5 | import { ElMessage } from "element-plus";
6 | import { tencentCloudCosApi } from "@/apis/admin/tencent-cloud-cos";
7 | import { filesApi } from "@/apis/admin/files";
8 | import { UploadTo } from "element-plus-admin/enums/upload-to";
9 |
10 | export default {
11 | setup() {
12 | const { params } = useRoute();
13 |
14 | const form = ref(null);
15 |
16 | const { isRequired } = useValidators();
17 |
18 | const currentPath = ref("");
19 |
20 | const cForm = reactive({
21 | model: {
22 | content: "",
23 | },
24 | rules: {
25 | content: [isRequired({ message: "内容不能为空" })],
26 | },
27 | });
28 |
29 | onMounted(async () => {
30 | await render(params.path);
31 | });
32 |
33 | onBeforeRouteUpdate(async (to, from, next) => {
34 | cForm.model.content = "";
35 | await render(to.params.path);
36 | next();
37 | });
38 |
39 | const render = async (path) => {
40 | currentPath.value = path.replace("-", "/");
41 |
42 | const { content } = await contentsApi.post({
43 | action: "findByPath",
44 | body: { path: currentPath.value },
45 | });
46 |
47 | cForm.model.content = content;
48 | console.log(cForm.model.content, content, "--");
49 | };
50 |
51 | const submit = async () => {
52 | form.value.validate(async (valid) => {
53 | if (valid) {
54 | await contentsApi.post({
55 | action: "updateByPath",
56 | body: {
57 | path: currentPath.value,
58 | content: cForm.model.content,
59 | },
60 | });
61 |
62 | ElMessage.success("保存成功");
63 | }
64 | });
65 | };
66 |
67 | return {
68 | tencentCloudCosApi,
69 | filesApi,
70 | UploadTo,
71 | form,
72 | cForm,
73 | currentPath,
74 | submit,
75 | };
76 | },
77 | };
78 |
--------------------------------------------------------------------------------
/src/views/orders/list/components/detail/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
52 |
53 | 取消
54 |
55 | 确定
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
--------------------------------------------------------------------------------
/src/views/articles/list/script.js:
--------------------------------------------------------------------------------
1 | import { useList } from "element-plus-admin/components/list/composables/use-list";
2 | import { useEnums } from "element-plus-admin/composables/use-enums";
3 | import { onMounted, ref } from "vue";
4 | import { articlesApi } from "@/apis/admin/articles";
5 | import Form from "./components/form/index.vue";
6 | import { ElMessage } from "element-plus";
7 | import { useRoute } from "vue-router";
8 |
9 | export default {
10 | components: {
11 | "b-form": Form,
12 | },
13 | setup() {
14 | const { params } = useRoute();
15 |
16 | const formRef = ref(null);
17 |
18 | const currentPath = ref("");
19 |
20 | const { enums } = useEnums();
21 |
22 | const {
23 | list,
24 | currentPage,
25 | cFilters,
26 | reRender,
27 | onPageChange,
28 | search,
29 | initialize,
30 | } = useList({
31 | api: articlesApi,
32 | autoRender: false,
33 | filters: {
34 | model: {
35 | title: { $like: "" },
36 | },
37 | rules: {},
38 | },
39 | extraQuery: () => ({
40 | order: [["order", "DESC"]],
41 | where: { path: currentPath.value, ...cFilters.model },
42 | }),
43 | beforeRouteUpdate(to, from) {
44 | if (to.params.path !== from.params.path) {
45 | currentPath.value = to.params.path.replace("-", "/");
46 | }
47 | },
48 | });
49 |
50 | onMounted(async () => {
51 | currentPath.value = params.path.replace("-", "/");
52 |
53 | await initialize({
54 | filters: {
55 | title: { $like: "" },
56 | },
57 | });
58 | });
59 |
60 | const del = async ({ id }) => {
61 | await articlesApi.delete({ id });
62 | ElMessage.success("删除成功");
63 | await reRender();
64 | };
65 |
66 | const getStatus = (row) =>
67 | row.draft && (!row.content || row.content === "
")
68 | ? "草稿"
69 | : "已发布";
70 |
71 | return {
72 | articlesApi,
73 | currentPath,
74 | formRef,
75 | enums,
76 | list,
77 | currentPage,
78 | cFilters,
79 | reRender,
80 | onPageChange,
81 | search,
82 | del,
83 | getStatus,
84 | };
85 | },
86 | };
87 |
--------------------------------------------------------------------------------
/src/views/jobs/list/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 | 刷新
9 | 新增
10 |
11 |
12 |
18 |
19 |
24 |
25 |
26 | 查询
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 | 编辑
46 |
47 |
48 | 删除
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
--------------------------------------------------------------------------------
/src/views/articles/list/components/form/script.js:
--------------------------------------------------------------------------------
1 | import { reactive, ref } from "vue";
2 | import { useHelpers } from "@/composables/use-helpers";
3 | import { articlesApi } from "@/apis/admin/articles";
4 | import { ElMessage } from "element-plus";
5 | import { useValidators } from "vue-validation";
6 | import { useFormDialog } from "element-plus-admin/composables/use-form-dialog";
7 | import { tencentCloudCosApi } from "@/apis/admin/tencent-cloud-cos";
8 | import { filesApi } from "@/apis/admin/files";
9 | import { UploadTo } from "element-plus-admin/enums/upload-to";
10 |
11 | export default {
12 | props: {
13 | path: String,
14 | },
15 | emits: ["ok"],
16 | setup(props, context) {
17 | const formRef = ref(null);
18 |
19 | const { deepCopy } = useHelpers();
20 |
21 | const { isRequired } = useValidators();
22 |
23 | const cDialog = reactive({
24 | visible: false,
25 | });
26 |
27 | const initialModel = {};
28 |
29 | const cForm = reactive({
30 | id: 0,
31 | model: deepCopy(initialModel),
32 | rules: {
33 | title: [isRequired({ label: "标题" })],
34 | content: [isRequired({ label: "内容" })],
35 | },
36 | });
37 |
38 | const { show, validate, validateField } = useFormDialog({
39 | cDialog,
40 | cForm,
41 | formRef,
42 | initialModel,
43 | });
44 |
45 | const getDraft = () => {
46 | cForm.model.content = cForm.model.draft;
47 | };
48 |
49 | const submit = async ({ draft = false } = {}) => {
50 | const { id, model } = await validate();
51 |
52 | const postData = { ...model, path: props.path };
53 |
54 | if (draft) {
55 | postData.draft = model.content;
56 | delete postData.content;
57 | }
58 |
59 | await articlesApi[id ? "put" : "post"]({
60 | id,
61 | body: postData,
62 | query: {
63 | where: { path: props.path },
64 | },
65 | });
66 | ElMessage.success(id ? "修改成功" : "新增成功");
67 | context.emit("ok");
68 | cDialog.visible = false;
69 | };
70 |
71 | return {
72 | tencentCloudCosApi,
73 | filesApi,
74 | UploadTo,
75 | formRef,
76 | cDialog,
77 | cForm,
78 | show,
79 | validateField,
80 | getDraft,
81 | submit,
82 | };
83 | },
84 | };
85 |
--------------------------------------------------------------------------------
/src/views/researches/list/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 | 刷新
9 | 新增
10 |
11 |
12 |
18 |
19 |
24 |
25 |
26 | 查询
27 |
28 |
29 |
30 |
31 |
32 |
33 |
36 |
37 |
38 |
39 |
40 |
41 | {{ $time.getTime(row.createdAt) }}
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 | 编辑
52 |
53 |
54 | 删除
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
--------------------------------------------------------------------------------
/src/views/articles/list/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 | 刷新
9 | 新增
10 |
11 |
12 |
18 |
19 |
24 |
25 |
26 | 查询
27 |
28 |
29 |
30 |
31 |
32 |
33 |
36 |
37 |
38 |
39 |
40 | {{ getStatus(row) }}
41 |
42 |
43 |
44 | {{ $time.getTime(row.createdAt) }}
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 | 编辑
55 |
56 |
57 | 删除
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
--------------------------------------------------------------------------------
/src/views/jobs/list/components/form/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
7 |
62 |
63 | 取消
64 | 确定
65 |
66 |
67 |
68 |
69 |
70 |
--------------------------------------------------------------------------------
/src/views/products/list/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 | 刷新
9 | 新增
10 |
11 |
12 |
18 |
19 |
24 |
25 |
26 |
31 |
32 |
33 | 查询
34 |
35 |
36 |
37 |
38 |
39 |
40 |
43 |
44 |
45 |
46 |
47 |
48 | {{ row.category ? row.category.name : "" }}
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 | 编辑
57 |
58 |
59 | 删除
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
--------------------------------------------------------------------------------
/src/views/team-members/list/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 | 刷新
9 | 新增
10 |
11 |
12 |
18 |
19 |
24 |
25 |
26 | 查询
27 |
28 |
29 |
30 |
31 |
32 |
33 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 | {{ $helpers.getItem(enums.Degree, "value", row.degree)["label"] }}
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 | 编辑
56 |
57 |
58 |
59 | 删除
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
--------------------------------------------------------------------------------
/src/views/app-upgrades/list/components/form/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
7 |
14 |
15 |
21 |
22 |
23 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
48 |
49 |
50 |
58 |
59 |
60 |
66 |
67 |
68 |
69 | 取消
70 | 确定
71 |
72 |
73 |
74 |
75 |
76 |
--------------------------------------------------------------------------------
/src/views/products/list/components/form/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
8 |
17 |
24 |
25 |
32 |
33 |
34 |
35 |
36 |
37 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
70 |
71 |
72 |
73 |
74 | 取消
75 | 确定
76 |
77 |
78 |
79 |
80 |
81 |
--------------------------------------------------------------------------------
/src/views/orders/list/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 | 刷新
9 |
10 |
11 |
17 |
18 |
23 |
24 |
25 | 查询
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 | {{ row.user.name || row.user.wxNickName }}
34 |
35 |
36 |
37 |
38 |
39 | {{ address.name }} {{ address.phoneNumber }}
40 | {{ address.location.name + address.room }}
41 |
42 |
43 |
44 |
45 | {{ row.amount }}元
46 |
47 |
48 |
49 | {{ $time.getTime(row.createdAt) }}
50 |
51 |
52 |
53 |
54 | {{ row.paidAt ? $time.getTime(row.paidAt) : "" }}
55 |
56 |
57 |
58 |
59 | {{
60 | $helpers.getItem(enums.OrderStatus, "value", row.status)["label"]
61 | }}
62 |
63 |
64 |
65 |
66 |
67 | 查看详情
68 |
69 |
70 |
71 | 删除
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
--------------------------------------------------------------------------------
/src/views/team-members/list/components/form/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
7 |
11 |
18 |
19 |
20 |
21 |
22 |
34 |
35 |
36 |
42 |
43 |
44 |
51 |
52 |
53 |
54 |
55 |
56 |
62 |
63 |
64 |
65 |
66 |
67 |
79 |
80 |
81 |
82 |
83 | 取消
84 | 确定
85 |
86 |
87 |
88 |
89 |
90 |
--------------------------------------------------------------------------------
/npm/element-plus-admin/components/layout/main/script.js:
--------------------------------------------------------------------------------
1 | import { onMounted, ref } from "vue";
2 | import { onBeforeRouteUpdate, useRoute } from "vue-router";
3 |
4 | export default {
5 | props: {
6 | menus: {
7 | type: Array,
8 | default: () => [],
9 | },
10 | },
11 | setup(props) {
12 | const { query } = useRoute();
13 |
14 | const menus = ref([{}, {}, {}, {}]);
15 |
16 | const renderMenus = (path) => {
17 | const routePaths = path.substr(1).split("/");
18 |
19 | menus.value = [{}, {}, {}, {}];
20 |
21 | props.menus.forEach((item1) => {
22 | item1.children.forEach((item2) => {
23 | const itemPaths = item2.path.substr(1).split("/");
24 |
25 | if (
26 | routePaths[0] === itemPaths[0] &&
27 | routePaths[1] === itemPaths[1]
28 | ) {
29 | menus.value[0] = item1;
30 | menus.value[1] = item2;
31 |
32 | if (item2.children) {
33 | item2.children.forEach((item3) => {
34 | const itemPaths = item3.path.substr(1).split("/");
35 |
36 | if (
37 | routePaths[2] === itemPaths[2] ||
38 | (itemPaths[2] === ":id" &&
39 | !isNaN(routePaths[2]) &&
40 | itemPaths[3] === routePaths[3])
41 | ) {
42 | menus.value[2] = {
43 | ...item3,
44 | route: {
45 | path: item3.path.replace(":id", routePaths[2]),
46 | query: {
47 | $menu1Title: query.$menu1Title,
48 | },
49 | },
50 | };
51 |
52 | if (item3.children) {
53 | item3.children.forEach((item4) => {
54 | const itemPaths = item4.path.substr(1).split("/");
55 |
56 | if (
57 | routePaths[4] === itemPaths[4] ||
58 | (itemPaths[4] === ":id" &&
59 | !isNaN(routePaths[4]) &&
60 | itemPaths[5] === routePaths[5])
61 | ) {
62 | menus.value[3] = {
63 | ...item4,
64 | route: {
65 | path: item4.path
66 | .replace(":id", routePaths[2])
67 | .replace(":id", routePaths[4]),
68 | query: {
69 | $menu1Title: query.$menu1Title,
70 | $menu2Title: query.$menu2Title,
71 | },
72 | },
73 | };
74 | }
75 | });
76 | }
77 | }
78 | });
79 | }
80 | }
81 | });
82 | });
83 | };
84 |
85 | onMounted(() => {
86 | renderMenus(useRoute().path);
87 | });
88 |
89 | onBeforeRouteUpdate(async (to, from, next) => {
90 | renderMenus(to.path);
91 | next();
92 | });
93 |
94 | return {
95 | menus,
96 | };
97 | },
98 | };
99 |
--------------------------------------------------------------------------------
/src/views/app-upgrades/list/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 | 刷新
9 | 新增
10 |
11 |
12 |
18 |
19 |
25 |
26 |
27 |
33 |
34 |
35 |
40 |
41 |
42 |
47 |
48 |
49 | 查询
50 |
51 |
52 |
53 |
54 |
55 |
56 | {{
57 | $helpers.getItem(enums.AppPlatform, "value", row.platform)["label"]
58 | }}
59 |
60 |
61 |
62 |
63 | {{
64 | $helpers.getItem(enums.AppPackageType, "value", row.packageType)[
65 | "label"
66 | ]
67 | }}
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 | {{
76 | $helpers.getItem(enums.PublishStatus, "value", row.status)["label"]
77 | }}
78 |
79 |
80 |
81 |
82 | 编辑
83 |
84 |
85 | 删除
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
--------------------------------------------------------------------------------
/npm/element-plus-admin/components/upload/script.js:
--------------------------------------------------------------------------------
1 | import { useConsts } from "@/composables/use-consts";
2 | import { useAuth } from "../../composables/use-auth";
3 | import { deepCopy } from "jt-helpers";
4 | import Files from "./files/index.vue";
5 | import { onMounted, reactive } from "vue";
6 | import { useCos } from "./composables/use-cos";
7 |
8 | const { ApiUrl } = useConsts();
9 | const { getHeaders } = useAuth();
10 |
11 | export default {
12 | components: {
13 | "cc-files": Files,
14 | },
15 | props: {
16 | action: {
17 | type: String,
18 | default: `${ApiUrl}/admin/files/actions/upload`,
19 | },
20 | headers: {
21 | type: Object,
22 | default: () => getHeaders(),
23 | },
24 | data: Object,
25 | multiple: {
26 | type: Boolean,
27 | default: false,
28 | },
29 | placeholder: {
30 | type: String,
31 | default: "请选择文件",
32 | },
33 | buttonSize: {
34 | type: String,
35 | default: "default",
36 | },
37 | buttonClass: String,
38 | showUploaded: {
39 | type: Boolean,
40 | default: true,
41 | },
42 | fileDir: {
43 | type: String,
44 | default: "",
45 | },
46 | cosConfig: {
47 | type: Object,
48 | default: () => null,
49 | },
50 | value: {
51 | type: [Number, Array],
52 | default: 0,
53 | },
54 | tip: {
55 | type: String,
56 | default: "",
57 | },
58 | officeViewerServiceUrl: String,
59 | },
60 | emits: ["update:value", "change", "success", "error"],
61 | setup(props, context) {
62 | const cUpload = reactive({
63 | progress: 0,
64 | });
65 |
66 | const cos = useCos({
67 | ...props.cosConfig,
68 | onProgress(progress) {
69 | cUpload.progress = progress;
70 | },
71 | });
72 |
73 | onMounted(async () => {
74 | if (props.cosConfig) {
75 | await cos.initialize();
76 | }
77 | });
78 |
79 | const update = (id) => {
80 | if (props.multiple) {
81 | const value = props.value ? [...props.value, id] : [id];
82 |
83 | context.emit("update:value", value);
84 | context.emit("change", value);
85 | } else {
86 | context.emit("update:value", id);
87 | context.emit("change", id);
88 | }
89 | };
90 |
91 | const beforeUpload = async (file) => {
92 | if (props.cosConfig) {
93 | update((await cos.upload(file, props.fileDir)).id);
94 | return Promise.reject();
95 | } else {
96 | return Promise.resolve();
97 | }
98 | };
99 |
100 | const onSuccess = (res) => {
101 | context.emit("success", res.data);
102 | update(res.data.id);
103 | };
104 |
105 | const onError = (err, file, fileList) => {
106 | context.emit("error", { err, file, fileList });
107 | };
108 |
109 | const onDelete = (index) => {
110 | if (props.multiple) {
111 | const value = deepCopy(props.value);
112 |
113 | value.splice(index, 1);
114 |
115 | context.emit("update:value", value);
116 | context.emit("change", value);
117 | } else {
118 | context.emit("update:value", undefined);
119 | context.emit("change", undefined);
120 | }
121 | };
122 |
123 | return {
124 | cUpload,
125 | beforeUpload,
126 | onSuccess,
127 | onError,
128 | onDelete,
129 | };
130 | },
131 | };
132 |
--------------------------------------------------------------------------------
/npm/element-plus-admin/components/upload/composables/use-cos.js:
--------------------------------------------------------------------------------
1 | import * as AliCloudOss from "ali-oss";
2 | import * as TencentCloudCos from "cos-js-sdk-v5";
3 | import { UploadTo } from "../../../enums/upload-to";
4 |
5 | export const useCos = ({
6 | cosApi,
7 | filesApi,
8 | uploadTo,
9 | region,
10 | bucket,
11 | onProgress,
12 | }) => {
13 | let client = null;
14 |
15 | const initialize = async () => {
16 | switch (uploadTo) {
17 | case UploadTo.AliCloudOss:
18 | {
19 | const {
20 | Credentials: { AccessKeyId, AccessKeySecret, SecurityToken },
21 | } = await cosApi.post({
22 | action: "getStsCredential",
23 | body: { region, bucket },
24 | });
25 |
26 | client = new AliCloudOss({
27 | region,
28 | bucket,
29 | accessKeyId: AccessKeyId,
30 | accessKeySecret: AccessKeySecret,
31 | stsToken: SecurityToken,
32 | });
33 | }
34 | break;
35 |
36 | case UploadTo.TencentCloudOss:
37 | {
38 | const {
39 | credentials: { tmpSecretId, tmpSecretKey, sessionToken },
40 | startTime,
41 | expiredTime,
42 | } = await cosApi.post({
43 | action: "getStsCredential",
44 | body: { region, bucket },
45 | });
46 |
47 | client = new TencentCloudCos({
48 | getAuthorization(options, callback) {
49 | callback({
50 | TmpSecretId: tmpSecretId,
51 | TmpSecretKey: tmpSecretKey,
52 | SecurityToken: sessionToken,
53 | StartTime: startTime,
54 | ExpiredTime: expiredTime,
55 | });
56 | },
57 | });
58 | }
59 | break;
60 |
61 | default:
62 | break;
63 | }
64 | };
65 |
66 | const upload = async (file, fileDir) => {
67 | const { name, type, size } = file;
68 | const ext = name.split(".").pop();
69 |
70 | const { id, date, uuid } = await filesApi.post({
71 | action: "create",
72 | body: { dir: fileDir },
73 | });
74 | const filePath = `${fileDir ? fileDir + "/" : ""}${date}/${uuid}.${ext}`;
75 |
76 | switch (uploadTo) {
77 | case UploadTo.AliCloudOss:
78 | await client.multipartUpload(filePath, file, {
79 | progress(p) {
80 | onProgress && onProgress(+(p * 100).toFixed(0));
81 | },
82 | parallel: 4,
83 | partSize: 1024 * 1024,
84 | meta: { year: 2020, people: "test" },
85 | mime: "text/plain",
86 | });
87 | break;
88 |
89 | case UploadTo.TencentCloudOss:
90 | await new Promise((resolve, reject) => {
91 | client.putObject(
92 | {
93 | Bucket: bucket,
94 | Region: region,
95 | Key: filePath,
96 | Body: file,
97 | onProgress({ percent }) {
98 | onProgress && onProgress(+(percent * 100).toFixed(0));
99 | },
100 | },
101 | (err, data) => {
102 | if (err) {
103 | reject(err);
104 | } else {
105 | resolve(data);
106 | }
107 | }
108 | );
109 | });
110 | break;
111 |
112 | default:
113 | break;
114 | }
115 |
116 | onProgress && onProgress(0);
117 |
118 | await filesApi.post({
119 | action: "update",
120 | body: { date, uuid, name, type, ext, size },
121 | });
122 |
123 | return { id };
124 | };
125 |
126 | return {
127 | client,
128 | initialize,
129 | upload,
130 | };
131 | };
132 |
--------------------------------------------------------------------------------
/npm/element-plus-admin/utils/create-api.js:
--------------------------------------------------------------------------------
1 | import axios from "axios";
2 | import NProgress from "nprogress";
3 | import "nprogress/nprogress.css";
4 | import { ElMessage } from "element-plus";
5 | import { debounce } from "throttle-debounce";
6 |
7 | NProgress.configure({ showSpinner: false });
8 |
9 | const startProgress = debounce(200, NProgress.start, { atBegin: true });
10 |
11 | const doneProgress = debounce(200, NProgress.done, { atBegin: false });
12 |
13 | const createRequest = ({ baseUrl, timeout = 5000, query, body }) => {
14 | const request = axios.create({
15 | baseURL: baseUrl || process.env.VUE_APP_API_URL,
16 | timeout,
17 | });
18 |
19 | request.interceptors.request.use(
20 | (config) => {
21 | const { method, params, data, showLoading } = config;
22 |
23 | showLoading && startProgress();
24 |
25 | if (params) {
26 | if (params.where) {
27 | if (params.where._vts) {
28 | delete params.where._vts;
29 | }
30 |
31 | config.params.where = formatWhere({
32 | ...(query().where || {}),
33 | ...params.where,
34 | });
35 | } else {
36 | config.params.where = formatWhere(query().where || {});
37 | }
38 | }
39 |
40 | if (data) {
41 | config.data = { ...body(), ...data };
42 | }
43 |
44 | ["include", "order", "attributes"].forEach((key) => {
45 | if (params && params[key]) {
46 | config.params[key] = JSON.stringify(params[key]);
47 | }
48 | });
49 |
50 | if (method === "get") {
51 | if (params) {
52 | config.params._ = new Date().getTime();
53 | } else {
54 | config.params = { _: new Date().getTime() };
55 | }
56 | }
57 |
58 | return config;
59 | },
60 | (error) => Promise.reject(error)
61 | );
62 |
63 | request.interceptors.response.use(
64 | (response) => {
65 | const {
66 | config,
67 | data: { data },
68 | } = response;
69 |
70 | config.showLoading && doneProgress();
71 |
72 | return data;
73 | },
74 | (error) => {
75 | const {
76 | response: { config, status, data },
77 | } = error;
78 |
79 | config.showLoading && doneProgress();
80 |
81 | if (status === 401) {
82 | window.location.href = "/#/logout";
83 | } else {
84 | if (config.showError) {
85 | if (data.error && data.error.message) {
86 | const { message } = data.error;
87 |
88 | if (typeof message === "string") {
89 | ElMessage.error(message);
90 | } else if (typeof message === "object") {
91 | ElMessage.error({
92 | message: Object.values(message).join("
"),
93 | dangerouslyUseHTMLString: true,
94 | });
95 | }
96 | } else {
97 | ElMessage.error("服务器内部错误");
98 | }
99 | }
100 | }
101 |
102 | return Promise.reject(error);
103 | }
104 | );
105 |
106 | return request;
107 | };
108 |
109 | const formatWhere = (obj) => {
110 | const ret = {};
111 |
112 | Object.keys(obj).forEach((attribute) => {
113 | ret[attribute] = {};
114 |
115 | Object.keys(obj[attribute]).forEach((operator) => {
116 | if (operator.substring(0, 2) === "$$") {
117 | ret[attribute] = {
118 | [operator.replace("$$", "$")]: obj[attribute][operator],
119 | };
120 | } else if (
121 | obj[attribute][operator] === undefined ||
122 | obj[attribute][operator] === "" ||
123 | obj[attribute][operator] === null
124 | ) {
125 | delete ret[attribute];
126 | } else if (operator === "$like") {
127 | ret[attribute][operator] = `%${obj[attribute][operator]}%`;
128 | } else {
129 | ret[attribute] = obj[attribute];
130 | }
131 | });
132 | });
133 |
134 | return JSON.stringify(ret);
135 | };
136 |
137 | export const createApi = ({
138 | baseUrl,
139 | url,
140 | headers = () => ({}),
141 | query = () => ({}),
142 | body = () => ({}),
143 | }) => {
144 | const request = createRequest({ baseUrl, query, body });
145 |
146 | return {
147 | config: { baseUrl, url, query, body },
148 |
149 | get: ({
150 | joinUrl = "",
151 | id,
152 | query = {},
153 | timeout = 5000,
154 | showLoading = true,
155 | showError = true,
156 | } = {}) =>
157 | request.get(`${url}${joinUrl}${id ? `/${id}` : ""}`, {
158 | headers: headers(),
159 | params: query,
160 | timeout,
161 | showLoading,
162 | showError,
163 | }),
164 |
165 | post: ({
166 | joinUrl = "",
167 | action,
168 | body = {},
169 | query = {},
170 | timeout = 5000,
171 | showLoading = true,
172 | showError = true,
173 | } = {}) =>
174 | request.post(
175 | action ? `${url}${joinUrl}/actions/${action}` : url + joinUrl,
176 | body,
177 | {
178 | headers: headers(),
179 | params: query,
180 | timeout,
181 | showLoading,
182 | showError,
183 | }
184 | ),
185 |
186 | put: ({
187 | joinUrl = "",
188 | id,
189 | body = {},
190 | query = {},
191 | timeout = 5000,
192 | showLoading = true,
193 | showError = true,
194 | } = {}) =>
195 | request.put(`${url}${joinUrl}/${id}`, body, {
196 | headers: headers(),
197 | params: query,
198 | timeout,
199 | showLoading,
200 | showError,
201 | }),
202 |
203 | delete: ({
204 | joinUrl = "",
205 | id,
206 | query = {},
207 | timeout = 5000,
208 | showLoading = true,
209 | showError = true,
210 | } = {}) =>
211 | request.delete(`${url}${joinUrl}/${id}`, {
212 | headers: headers(),
213 | params: query,
214 | timeout,
215 | showLoading,
216 | showError,
217 | }),
218 | };
219 | };
220 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## 关于
2 |
3 | #### 介绍
4 |
5 | 基于 Vue 3.0 + Element Plus 的后台前端解决方案,支持:开发调试、构建、代码规范校验等。
6 |
7 | #### 技术栈
8 |
9 | Vue、Webpack、ES6、Vue Router、Vuex、Sass、PostCSS 等。
10 |
11 | ## 命令
12 |
13 | ```bash
14 | # 安装依赖
15 | $ npm install
16 |
17 | # 开发调试
18 | $ npm run serve
19 |
20 | # 代码校验
21 | $ npm run lint
22 |
23 | # 构建
24 | $ npm run build
25 |
26 | # 从模板创建文件
27 | $ npm run newfile
28 | ```
29 |
30 | ## 规范
31 |
32 | #### 项目结构
33 |
34 | ```
35 | |- plop-templates 基本模板
36 | |- public 网站公共目录
37 | | |- favicon.ico 网站图标
38 | | |- index.html HTML 模板
39 | |- src 源码目录
40 | | |- assets 待编译的静态资源
41 | | |- images 图片
42 | | |- components 组件图片
43 | | |- styles 样式
44 | | |- global 全局样式
45 | | |- components 组件样式
46 | | |- iconfont.scss iconfont
47 | | |- reset CSS Reset
48 | | |- utils Sass 工具
49 | | |- variables Sass 变量
50 | | |- components 公共组件
51 | | |- composables 公用 composiable 函数
52 | | |- models 数据模型
53 | | |- router 路由配置
54 | | |- routes 各个视图的路由配置
55 | | |- store 状态管理
56 | | |- modules 状态管理(指定命名空间)
57 | | |- auth.js auth 状态管理
58 | | |- utils JS 工具
59 | | |- views 视图
60 | | |- home 首页
61 | | |- App.vue 页面入口
62 | | |- main.js 程序入口
63 | ```
64 |
65 | #### 公共组件规范
66 |
67 | 公共组件放在 /src/components 下。
68 |
69 | ```
70 | |- my-component my component 组件
71 | | |- index.vue my component 的入口
72 | | |- script.js my component 的脚本
73 | | |- style.scss my component 的样式
74 | | |- utils my component 的 JS 工具
75 | | |- components my component 的子组件
76 | | |- child-component my component 的子组件 child component
77 | ```
78 |
79 | #### 视图规范
80 |
81 | 视图,也就是页面,放在 /src/views 下。规范和公共组件一致。
82 |
83 | ## 链接
84 |
85 | #### 文档
86 |
87 | - [Vue3 中文文档(官方)](https://v3.cn.vuejs.org/)
88 | - [Vue3 中文文档](https://vue3js.cn/docs/zh/)
89 | - [Vue3 组合式 API(官方)](https://composition-api.vuejs.org/zh/api.html)
90 | - [Vue3 组合式 API](https://vue3js.cn/vue-composition-api/)
91 | - [Vue3 组合式 API 高阶指南](https://vue3js.cn/docs/zh/guide/composition-api-introduction.html)
92 | - [Vue3 资源推荐](https://vue3js.cn/)
93 | - [Vue3 迁移指南](https://vue3js.cn/docs/zh/guide/migration/introduction.html)
94 | - [Vue3 API 参考](https://vue3js.cn/docs/zh/api/)
95 | - [Vue3 风格指南](https://vue3js.cn/docs/zh/style-guide/)
96 |
97 | #### 配套工具
98 |
99 | - [Vue CLI](https://cli.vuejs.org/migrating-from-v3/)
100 | - [Vue Router](https://vue3js.cn/router4/)
101 | - [Vuex](https://vue3js.cn/vuex/zh/)
102 |
103 | #### 视频
104 |
105 | - [米修在线 - Vue3 快速上手指南](https://www.bilibili.com/video/BV1HT4y137m3)
106 | - [李南江 - Vue3 正式版教程](https://www.bilibili.com/video/BV14k4y117LL)
107 | - [哈默聊前端 - Vue3](https://space.bilibili.com/492976859/video)
108 | - [大地 - Vue3 教程](https://www.bilibili.com/video/BV1zt411e7fp)
109 | - [Young 村长 - Composition API + 深度解读](https://www.bilibili.com/video/BV1my4y1m7sz)
110 | - [Young 村长 - Vue3 光速上手](https://www.bilibili.com/video/BV1Wh411X7Xp)
111 | - [Young 村长 - 项目打包、部署、CI/CD](https://www.bilibili.com/video/BV1Wh411X7Xp?p=30)
112 |
113 | #### 链接
114 |
115 | - [【Vue3 官方教程】🎄 万字笔记 | 同步导学视频 ](https://juejin.cn/post/6909247394904702984)
116 | - [vue3-study](https://github.com/su37josephxia/vue3-study)
117 | - [Vue-Mastery 学习笔记](https://www.yuque.com/nxtt7g/kompdt)
118 | - [@vue/composition-api - 用于提供组合式 API 的 Vue 2 插件](https://github.com/vuejs/composition-api/blob/master/README.zh-CN.md)
119 | - [@vue/composition-api 速成课](https://blog.csdn.net/frontend_frank/article/details/108786784)
120 | - [Vue Class Component v8 - The next Vue Class Component for Vue v3.](https://github.com/vuejs/vue-class-component/tree/next)
121 | - [Vue3 实战笔记](https://juejin.cn/post/6909632635665039367)
122 | - [快速使用 Vue3 最新的 15 个常用 API](https://juejin.cn/post/6897030228867022856)
123 |
124 | #### 开源
125 |
126 | - [View Shadcn UI](https://view-shadcn-ui.devlive.org/)
127 | - [element-plus-kit](https://github.com/mitjs/element-plus-kit)
128 | - [element-plus-x](https://github.com/SorrowX/element-plus-x)
129 | - [agile-admin](https://github.com/gmingchen/agile-admin)
130 | - [element-plus-theme](https://github.com/ele-admin/element-plus-theme)
131 | - [miitvip-vue-admin-manager](https://github.com/lirongtong/miitvip-vue-admin-manager)
132 | - [nuxt-shadcn-dashboard](https://github.com/dianprata/nuxt-shadcn-dashboard)
133 | - [M-Admin](https://github.com/sina-xys-felix/M-Admin)
134 | - [opentiny](https://opentiny.design/)
135 | - [art-design-pro](https://www.lingchen.kim/art-design-pro/docs/zh/)
136 | - [una-ui](https://github.com/una-ui/una-ui)
137 | - [sz-admin](https://github.com/feiyuchuixue/sz-admin)
138 | - [continew-admin](https://github.com/continew-org/continew-admin)
139 | - [nova-admin](https://github.com/chansee97/nova-admin)
140 | - [Snowy](https://github.com/xiaonuobase/Snowy)
141 | - [smart-admin](https://github.com/1024-lab/smart-admin)
142 | - [shadcn-admin](https://github.com/satnaing/shadcn-admin)
143 | - [primevue](https://github.com/primefaces/primevue)
144 | - [volt](https://volt.primevue.org/)
145 | - [vue-pure-admin](https://github.com/pure-admin/vue-pure-admin)
146 | - [vue-vben-admin](https://github.com/vbenjs/vue-vben-admin)
147 | - [vue3-antdv-admin](https://github.com/buqiyuan/vue3-antdv-admin)
148 | - [vue-admin-better](https://github.com/chuzhixin/vue-admin-better)
149 | - [vue-element-ui-admin(Vue3+ScriptSetup)](https://github.com/xusenlin/vue-element-ui-admin)
150 | - [v3-admin-vite](https://github.com/un-pany/v3-admin-vite)
151 | - [vue-element-plus-admin](https://github.com/kailong321200875/vue-element-plus-admin)
152 | - [SnowAdmin](https://gitee.com/wang_fan_w/SnowAdmin)
153 | - [vue3-element-admin](https://github.com/youlaitech/vue3-element-admin)
154 | - [naive-ui-admin](https://github.com/jekip/naive-ui-admin)
155 | - [soybean-admin](https://github.com/soybeanjs/soybean-admin)
156 | - [后台集合](http://vue.easydo.work/)
157 | - [Arco Design Pro](https://github.com/arco-design/arco-design-pro-vue)
158 | - [fantastic-admin](https://fantastic-admin.hurui.me/)
159 | - [AdminLTE](https://github.com/ColorlibHQ/AdminLTE)
160 | - [gin-vue-admin](https://github.com/flipped-aurora/gin-vue-admin)
161 | - [panis-admin](https://github.com/paynezhuang/panis-admin)
162 |
163 | #### 交互参考
164 |
165 | - [mineadmin](https://www.mineadmin.com/)
166 | - [kottster](https://kottster.app/)
167 | - [naive-ui-pro](https://naive-ui-pro.pro-components.cn/home)
168 | - [metis-ui](https://github.com/metisjs/metis-ui)
169 | - [FinCRM](https://github.com/MrXujiang/FinCRM)
170 | - [nuxt-ui-pro/dashboard](https://github.com/nuxt-ui-pro/dashboard)
171 | - [orangeforms](https://www.orangeforms.com/)
172 | - [shop-vite](https://vuejs-core.cn/shop-vite/)
173 | - [semi](https://semi.design/zh-CN/)
174 | - [Cool JS](https://cool-js.com/)
175 | - [Ant Design Pro](https://pro.ant.design/zh-CN/)
176 | - [Ant Design Pro Components](https://procomponents.ant.design/)
177 | - [View UI 专业版](https://pro.iviewui.com/)
178 | - [复盘20+基于React的开源管理后台&插件](https://juejin.cn/post/7304919237983404083)
179 | - [vue-admin-arco](https://github.com/zxwk1998/vue-admin-arco)
180 |
--------------------------------------------------------------------------------
/npm/element-plus-admin/components/list/composables/use-list.js:
--------------------------------------------------------------------------------
1 | import { computed, onMounted, reactive, ref } from "vue";
2 | import { onBeforeRouteUpdate, useRoute, useRouter } from "vue-router";
3 | import { useConsts } from "@/composables/use-consts";
4 | import { deepCopy } from "jt-helpers";
5 | import { useHelpers } from "./use-helpers";
6 | import { ElMessage, ElMessageBox } from "element-plus";
7 |
8 | export const useList = ({
9 | onRendered,
10 | routeMode = true,
11 | autoRender = true,
12 | filtersAsKeyValue = false,
13 | pageSize = useConsts().PageSize,
14 | api,
15 | filters = {},
16 | data = {},
17 | extraQuery = {},
18 | mounted = () => {},
19 | beforeRouteUpdate = () => {},
20 | } = {}) => {
21 | const route = useRoute();
22 | const router = useRouter();
23 | const { formatFilters, encode, decode } = useHelpers();
24 | const list = reactive({ items: [], total: 0 });
25 | const loading = ref(false);
26 | const currentPageSize = ref(pageSize);
27 | const currentPage = ref(1);
28 | const cFilters = reactive({
29 | ref: (el) => (filtersRef = el),
30 | ...filters,
31 | });
32 | const filtersModel = filters.model;
33 |
34 | let filtersRef = null;
35 |
36 | const state = reactive({
37 | checkAll: false,
38 | checkedItems: {},
39 | });
40 |
41 | const checkedIds = computed(() =>
42 | Object.keys(state.checkedItems)
43 | .filter((key) => state.checkedItems[key])
44 | .map((item) => parseInt(item, 10))
45 | );
46 |
47 | const getQuery = (query) => {
48 | const {
49 | currentPageSize = pageSize,
50 | currentPage = 1,
51 | filters = deepCopy(filtersModel),
52 | } = decode(query.$list);
53 |
54 | return { currentPageSize, currentPage, filters };
55 | };
56 |
57 | const render = async ({
58 | currentPageSize = pageSize,
59 | currentPage = 1,
60 | filters = deepCopy(filtersModel),
61 | } = {}) => {
62 | const query = {
63 | offset: (currentPage - 1) * currentPageSize,
64 | limit: currentPageSize,
65 | ...data,
66 | };
67 |
68 | if (filtersAsKeyValue) {
69 | Object.assign(query, filters);
70 | } else {
71 | query.where = formatFilters(filters);
72 | }
73 |
74 | const { items, total, ...extra } = await api.get({
75 | query: {
76 | ...query,
77 | ...(typeof extraQuery === "function" ? extraQuery() : extraQuery),
78 | },
79 | });
80 |
81 | list.items = items;
82 | list.total = total;
83 | list.extra = extra;
84 |
85 | onRendered && onRendered();
86 | };
87 |
88 | const initialize = async ({ filters = deepCopy(filtersModel) }) => {
89 | currentPageSize.value = pageSize;
90 | currentPage.value = 1;
91 | cFilters.model = filters;
92 | await render({ filters });
93 | };
94 |
95 | onMounted(async () => {
96 | await mounted();
97 |
98 | if (!autoRender) return;
99 |
100 | const query = getQuery(route.query);
101 |
102 | currentPageSize.value = query.currentPageSize;
103 | currentPage.value = query.currentPage;
104 | cFilters.model = query.filters;
105 |
106 | await render(query);
107 | });
108 |
109 | onBeforeRouteUpdate(async (to, from, next) => {
110 | await beforeRouteUpdate(to, from);
111 |
112 | if (!routeMode) return;
113 |
114 | filtersRef && filtersRef.resetFields();
115 |
116 | const query = getQuery(to.query);
117 |
118 | currentPageSize.value = query.currentPageSize;
119 | currentPage.value = query.currentPage;
120 | cFilters.model = query.filters;
121 |
122 | await render(query);
123 |
124 | next();
125 | });
126 |
127 | const reRender = async () => {
128 | await render(getQuery(route.query));
129 | };
130 |
131 | const search = async (filters = {}) => {
132 | filtersRef &&
133 | filtersRef.validate(async (valid) => {
134 | if (!valid) return;
135 |
136 | loading.value = true;
137 |
138 | if (routeMode) {
139 | await router.replace({
140 | query: {
141 | ...route.query,
142 | $list: encode({
143 | currentPageSize: currentPageSize.value,
144 | currentPage: 1,
145 | filters: { ...cFilters.model, ...filters },
146 | }),
147 | },
148 | });
149 | } else {
150 | currentPage.value = 1;
151 | cFilters.model = { ...cFilters.model, ...filters };
152 |
153 | await render({
154 | currentPageSize: currentPageSize.value,
155 | currentPage: 1,
156 | filters: cFilters.model,
157 | });
158 | }
159 |
160 | loading.value = false;
161 | });
162 | };
163 |
164 | const del = async ({ id }) => {
165 | await api.delete({ id });
166 | ElMessage.success("删除成功");
167 | await reRender();
168 | };
169 |
170 | const bulkDel = async () => {
171 | if (!checkedIds.value.length) {
172 | ElMessage.error("未选中数据");
173 | return;
174 | }
175 |
176 | await ElMessageBox.confirm("确认删除选中数据?", "提示", {
177 | confirmButtonText: "删除",
178 | confirmButtonClass: "el-button--danger",
179 | cancelButtonText: "取消",
180 | type: "warning",
181 | });
182 |
183 | await api.post({
184 | action: "bulkDestroy",
185 | body: { ids: checkedIds.value },
186 | });
187 | ElMessage.success("删除成功");
188 | await reRender();
189 | };
190 |
191 | const onPageChange = async (current) => {
192 | if (!loading.value) {
193 | if (routeMode) {
194 | const query = getQuery(route.query);
195 |
196 | await router.replace({
197 | query: {
198 | ...route.query,
199 | $list: encode({
200 | ...query,
201 | currentPageSize: currentPageSize.value,
202 | currentPage: current,
203 | }),
204 | },
205 | });
206 | } else {
207 | loading.value = true;
208 | currentPage.value = current;
209 | await render({
210 | currentPageSize: currentPageSize.value,
211 | currentPage: current,
212 | filters: cFilters.model,
213 | });
214 | loading.value = false;
215 | }
216 | }
217 | };
218 |
219 | const onPageSizeChange = async (pageSize) => {
220 | if (!loading.value) {
221 | if (routeMode) {
222 | currentPageSize.value = pageSize;
223 | currentPage.value = 1;
224 |
225 | const query = getQuery(route.query);
226 |
227 | await router.replace({
228 | query: {
229 | ...route.query,
230 | $list: encode({
231 | ...query,
232 | currentPageSize: pageSize,
233 | currentPage: 1,
234 | }),
235 | },
236 | });
237 | } else {
238 | loading.value = true;
239 | currentPageSize.value = pageSize;
240 | currentPage.value = 1;
241 |
242 | await render({
243 | currentPageSize: pageSize,
244 | currentPage: 1,
245 | filters: cFilters.model,
246 | });
247 | loading.value = false;
248 | }
249 | }
250 | };
251 |
252 | const onCheckAllChange = () => {
253 | list.items.forEach((item) => {
254 | state.checkedItems[item.id + ""] = state.checkAll;
255 | });
256 | };
257 |
258 | const onCheckChange = () => {
259 | let isAllChecked = true;
260 |
261 | list.items.forEach((item) => {
262 | if (!state.checkedItems[item.id + ""]) {
263 | isAllChecked = false;
264 | }
265 | });
266 |
267 | state.checkAll = isAllChecked;
268 | };
269 |
270 | return {
271 | list,
272 | currentPageSize,
273 | currentPage,
274 | cFilters,
275 | state,
276 | checkedIds,
277 | render,
278 | initialize,
279 | reRender,
280 | search,
281 | del,
282 | bulkDel,
283 | onPageChange,
284 | onPageSizeChange,
285 | onCheckAllChange,
286 | onCheckChange,
287 | };
288 | };
289 |
--------------------------------------------------------------------------------