同步admin

This commit is contained in:
CQ 2025-11-13 09:44:49 +08:00
parent 16a8825962
commit ef43d9e616
21 changed files with 1148 additions and 702 deletions

143
admin/DEVELOPMENT_GUIDE.md Normal file
View File

@ -0,0 +1,143 @@
# NiuCloud Admin 开发规范
## 技术栈区分
本项目采用前后端分离架构,包含两个主要前端部分:
1. **PC端后台管理系统**
- 框架: Vue 3 + TypeScript + Vite
- UI组件库: Element Plus
- 状态管理: Pinia
- 样式处理: SCSS + Tailwind CSS
2. **移动端应用**
- 框架: uni-app + Vue 3 + TypeScript
- UI组件库: uview-plus
- 状态管理: Pinia
- 样式处理: SCSS + Windi CSS
## 关键组件使用规范
### 消息提示组件
**重要注意事项:请根据开发平台选择正确的消息提示组件!**
#### PC端 (admin目录)
- **必须使用Element Plus的消息提示组件**而不是uni-app的方法
- 主要组件包括:`ElMessage``ElMessageBox``ElNotification`
- 导入方式:`import { ElMessage, ElMessageBox } from 'element-plus'`
- 使用示例:
```typescript
import { ElMessage } from 'element-plus'
// 成功消息
ElMessage.success('操作成功')
// 错误消息
ElMessage.error('操作失败')
// 确认对话框
ElMessageBox.confirm('确定要执行此操作吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
// 用户点击确认后的逻辑
}).catch(() => {
// 用户点击取消后的逻辑
})
```
#### 移动端 (uni-app目录)
- **使用uni-app提供的API**进行消息提示
- 主要方法包括:`uni.showToast``uni.showModal``uni.showLoading`
- 使用示例:
```typescript
// 成功提示
uni.showToast({
title: '操作成功',
icon: 'success',
duration: 2000
})
// 模态对话框
uni.showModal({
title: '提示',
content: '确定要执行此操作吗?',
success: (res) => {
if (res.confirm) {
// 用户点击确认后的逻辑
}
}
})
```
## API请求规范
### PC端API请求
- 使用`@/utils/request.ts`封装的请求工具
- 支持`showSuccessMessage``showErrorMessage`选项控制消息显示
- 示例:
```typescript
import request from '@/utils/request'
// GET请求
export function getOrderList(params: Record<string, any>) {
return request.get('order/list', params)
}
// POST请求带成功消息
export function createOrder(params: Record<string, any>) {
return request.post('order/create', params, { showSuccessMessage: true })
}
```
### 移动端API请求
- 使用uni-app的`uni.request`或封装的请求工具
- 示例:
```typescript
// 发送请求
uni.request({
url: 'https://example.com/api/order/list',
method: 'GET',
data: {
page: 1,
limit: 10
},
success: (res) => {
// 处理成功响应
},
fail: (err) => {
// 处理请求失败
}
})
```
## 代码风格规范
1. **文件命名**
- 组件文件PascalCase`OrderList.vue`
- 普通文件kebab-case 或 camelCase`api-service.ts``commonUtils.ts`
2. **TypeScript规范**
- 为函数参数、返回值和重要变量添加明确的类型注解
- 使用接口 (interface) 定义复杂数据结构
- 避免 `any` 类型的滥用
3. **Vue组件规范**
- 使用 Vue 3 Composition API 和 `<script setup lang="ts">` 语法
- 组件样式建议使用 scoped 属性或 CSS Modules
## 国际化规范
- PC端使用Vue I18n进行国际化语言文件位于`src/lang`目录
- 移动端同样使用Vue I18n语言文件位于`src/app/locale`目录
- 使用`t('key')`函数获取翻译文本
## 其他重要规范
- 严格遵循RESTful API设计规范
- 统一处理API响应数据和错误情况
- 代码提交前确保通过TypeScript类型检查
- 组件开发遵循高内聚低耦合原则
- 优先复用现有组件和工具函数

View File

@ -9,12 +9,18 @@
"version": "1.0.0", "version": "1.0.0",
"dependencies": { "dependencies": {
"@element-plus/icons-vue": "2.0.10", "@element-plus/icons-vue": "2.0.10",
"@fullcalendar/core": "^6.1.19",
"@fullcalendar/daygrid": "^6.1.19",
"@fullcalendar/interaction": "^6.1.19",
"@fullcalendar/vue3": "^6.1.19",
"@heroicons/vue": "^2.2.0",
"@highlightjs/vue-plugin": "2.1.0", "@highlightjs/vue-plugin": "2.1.0",
"@types/lodash-es": "4.17.6", "@types/lodash-es": "4.17.6",
"@vueuse/core": "9.12.0", "@vueuse/core": "9.12.0",
"axios": "1.4.0", "axios": "1.4.0",
"crypto-js": "4.1.1", "crypto-js": "4.1.1",
"css-color-function": "1.3.3", "css-color-function": "1.3.3",
"date-fns": "^4.1.0",
"day": "^0.0.2", "day": "^0.0.2",
"echarts": "5.4.1", "echarts": "5.4.1",
"element-plus": "^2.7.4", "element-plus": "^2.7.4",
@ -963,6 +969,47 @@
"resolved": "https://registry.npmmirror.com/@floating-ui/utils/-/utils-0.1.6.tgz", "resolved": "https://registry.npmmirror.com/@floating-ui/utils/-/utils-0.1.6.tgz",
"integrity": "sha512-OfX7E2oUDYxtBvsuS4e/jSn4Q9Qb6DzgeYtsAdkPZ47znpoNsMgZw0+tVijiv3uGNR6dgNlty6r9rzIzHjtd/A==" "integrity": "sha512-OfX7E2oUDYxtBvsuS4e/jSn4Q9Qb6DzgeYtsAdkPZ47znpoNsMgZw0+tVijiv3uGNR6dgNlty6r9rzIzHjtd/A=="
}, },
"node_modules/@fullcalendar/core": {
"version": "6.1.19",
"resolved": "https://registry.npmmirror.com/@fullcalendar/core/-/core-6.1.19.tgz",
"integrity": "sha512-z0aVlO5e4Wah6p6mouM0UEqtRf1MZZPt4mwzEyU6kusaNL+dlWQgAasF2cK23hwT4cmxkEmr4inULXgpyeExdQ==",
"dependencies": {
"preact": "~10.12.1"
}
},
"node_modules/@fullcalendar/daygrid": {
"version": "6.1.19",
"resolved": "https://registry.npmmirror.com/@fullcalendar/daygrid/-/daygrid-6.1.19.tgz",
"integrity": "sha512-IAAfnMICnVWPjpT4zi87i3FEw0xxSza0avqY/HedKEz+l5MTBYvCDPOWDATpzXoLut3aACsjktIyw9thvIcRYQ==",
"peerDependencies": {
"@fullcalendar/core": "~6.1.19"
}
},
"node_modules/@fullcalendar/interaction": {
"version": "6.1.19",
"resolved": "https://registry.npmmirror.com/@fullcalendar/interaction/-/interaction-6.1.19.tgz",
"integrity": "sha512-GOciy79xe8JMVp+1evAU3ytdwN/7tv35t5i1vFkifiuWcQMLC/JnLg/RA2s4sYmQwoYhTw/p4GLcP0gO5B3X5w==",
"peerDependencies": {
"@fullcalendar/core": "~6.1.19"
}
},
"node_modules/@fullcalendar/vue3": {
"version": "6.1.19",
"resolved": "https://registry.npmmirror.com/@fullcalendar/vue3/-/vue3-6.1.19.tgz",
"integrity": "sha512-j5eUSxx0xIy3ADljo0f5B9PhjqXnCQ+7nUMPfsslc2eGVjp4F74YvY3dyd6OBbg13IvpsjowkjncGipYMQWmTA==",
"peerDependencies": {
"@fullcalendar/core": "~6.1.19",
"vue": "^3.0.11"
}
},
"node_modules/@heroicons/vue": {
"version": "2.2.0",
"resolved": "https://registry.npmmirror.com/@heroicons/vue/-/vue-2.2.0.tgz",
"integrity": "sha512-G3dbSxoeEKqbi/DFalhRxJU4mTXJn7GwZ7ae8NuEQzd1bqdd0jAbdaBZlHPcvPD2xI1iGzNVB4k20Un2AguYPw==",
"peerDependencies": {
"vue": ">= 3"
}
},
"node_modules/@highlightjs/vue-plugin": { "node_modules/@highlightjs/vue-plugin": {
"version": "2.1.0", "version": "2.1.0",
"resolved": "https://registry.npmmirror.com/@highlightjs/vue-plugin/-/vue-plugin-2.1.0.tgz", "resolved": "https://registry.npmmirror.com/@highlightjs/vue-plugin/-/vue-plugin-2.1.0.tgz",
@ -2624,6 +2671,15 @@
"resolved": "https://registry.npmmirror.com/csstype/-/csstype-2.6.21.tgz", "resolved": "https://registry.npmmirror.com/csstype/-/csstype-2.6.21.tgz",
"integrity": "sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w==" "integrity": "sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w=="
}, },
"node_modules/date-fns": {
"version": "4.1.0",
"resolved": "https://registry.npmmirror.com/date-fns/-/date-fns-4.1.0.tgz",
"integrity": "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/kossnocorp"
}
},
"node_modules/day": { "node_modules/day": {
"version": "0.0.2", "version": "0.0.2",
"resolved": "https://registry.npmmirror.com/day/-/day-0.0.2.tgz", "resolved": "https://registry.npmmirror.com/day/-/day-0.0.2.tgz",
@ -5003,6 +5059,15 @@
"integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
"dev": true "dev": true
}, },
"node_modules/preact": {
"version": "10.12.1",
"resolved": "https://registry.npmmirror.com/preact/-/preact-10.12.1.tgz",
"integrity": "sha512-l8386ixSsBdbreOAkqtrwqHwdvR35ID8c3rKPa8lCWuO86dBi32QWHV4vfsZK1utLLFMvw+Z5Ad4XLkZzchscg==",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/preact"
}
},
"node_modules/prelude-ls": { "node_modules/prelude-ls": {
"version": "1.2.1", "version": "1.2.1",
"resolved": "https://registry.npmmirror.com/prelude-ls/-/prelude-ls-1.2.1.tgz", "resolved": "https://registry.npmmirror.com/prelude-ls/-/prelude-ls-1.2.1.tgz",

View File

@ -10,12 +10,18 @@
}, },
"dependencies": { "dependencies": {
"@element-plus/icons-vue": "2.0.10", "@element-plus/icons-vue": "2.0.10",
"@fullcalendar/core": "^6.1.19",
"@fullcalendar/daygrid": "^6.1.19",
"@fullcalendar/interaction": "^6.1.19",
"@fullcalendar/vue3": "^6.1.19",
"@heroicons/vue": "^2.2.0",
"@highlightjs/vue-plugin": "2.1.0", "@highlightjs/vue-plugin": "2.1.0",
"@types/lodash-es": "4.17.6", "@types/lodash-es": "4.17.6",
"@vueuse/core": "9.12.0", "@vueuse/core": "9.12.0",
"axios": "1.4.0", "axios": "1.4.0",
"crypto-js": "4.1.1", "crypto-js": "4.1.1",
"css-color-function": "1.3.3", "css-color-function": "1.3.3",
"date-fns": "^4.1.0",
"day": "^0.0.2", "day": "^0.0.2",
"echarts": "5.4.1", "echarts": "5.4.1",
"element-plus": "^2.7.4", "element-plus": "^2.7.4",

View File

@ -367,12 +367,16 @@ export function getCashOutDetail(id: number) {
export function memberAudit(params: Record<string, any>) { export function memberAudit(params: Record<string, any>) {
return request.put(`member/cash_out/audit/${ params.id }/${ params.action }`, params, { showSuccessMessage: true }) return request.put(`member/cash_out/audit/${ params.id }/${ params.action }`, params, { showSuccessMessage: true })
} }
/** /**
* *
* @param params * @param params
*/ */
export function memberCancel(params: Record<string, any>) { export function memberCancel(params: Record<string, any>) {
return request.put(`member/cash_out/cancel/${params.id}`, params, { showSuccessMessage: true,showErrorMessage: true }) return request.put(`member/cash_out/cancel/${ params.id }`, params, {
showSuccessMessage: true,
showErrorMessage: true
})
} }
@ -391,6 +395,7 @@ export function memberTransfer(params: Record<string, any>) {
export function memberRemark(params: Record<string, any>) { export function memberRemark(params: Record<string, any>) {
return request.put(`member/cash_out/remark/${ params.id }`, params, { showSuccessMessage: true }) return request.put(`member/cash_out/remark/${ params.id }`, params, { showSuccessMessage: true })
} }
/** /**
* *
* @param id * @param id
@ -453,6 +458,7 @@ export function getGrowthRuleDict() {
export function getPointRuleDict() { export function getPointRuleDict() {
return request.get(`member/dict/point_rule`) return request.get(`member/dict/point_rule`)
} }
/***************************************************** 会员等级 ****************************************************/ /***************************************************** 会员等级 ****************************************************/
/** /**
@ -512,14 +518,14 @@ export function getMemberLevelAll() {
* *
*/ */
export function getMemberBenefitsContent() { export function getMemberBenefitsContent() {
return request.get(`member/benefits/content`); return request.post(`member/benefits/content`);
} }
/** /**
* *
*/ */
export function getMemberGiftsContent(params: Record<string, any>) { export function getMemberGiftsContent(params: Record<string, any>) {
return request.get(`member/gifts/content`, { params }); return request.post(`member/gifts/content`, params);
} }
/** /**

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 518 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 478 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 834 B

View File

@ -29,5 +29,8 @@
"encodingAesKeyPlaceholder": "请输入EncodingAESKey", "encodingAesKeyPlaceholder": "请输入EncodingAESKey",
"cleartextModeTips": "明文模式下,不使用消息体加解密功能,安全系数较低", "cleartextModeTips": "明文模式下,不使用消息体加解密功能,安全系数较低",
"compatibleModeTips": "兼容模式下,明文、密文将共存,方便开发者调试和维护", "compatibleModeTips": "兼容模式下,明文、密文将共存,方便开发者调试和维护",
"safeModeTips": "安全模式下,消息包为纯密文,需要开发者加密和解密,安全系数高" "safeModeTips": "安全模式下,消息包为纯密文,需要开发者加密和解密,安全系数高",
"wechatBaseUri": "借权域名",
"wechatBaseUriPlaceholder": "",
"wechatBaseUriTips": "默认留空填写后将替换https://open.weixin.gg.com/获取授权!"
} }

View File

@ -61,7 +61,7 @@ const getMarketingList = async () => {
// marketingList.value = res.data // marketingList.value = res.data
loading.value = false loading.value = false
} }
getMarketingList() // getMarketingList()
const toLink = (item: any) => { const toLink = (item: any) => {
if (item.url) { if (item.url) {

View File

@ -36,6 +36,11 @@
<el-input v-model.trim="formData.app_secret" :placeholder="t('appSecretPlaceholder')" class="input-width" clearable /> <el-input v-model.trim="formData.app_secret" :placeholder="t('appSecretPlaceholder')" class="input-width" clearable />
<div class="form-tip">{{ t('wechatAppsecretTips') }}</div> <div class="form-tip">{{ t('wechatAppsecretTips') }}</div>
</el-form-item> </el-form-item>
<el-form-item :label="t('wechatBaseUri')" prop="base_uri" v-if="!formData.is_authorization">
<el-input v-model.trim="formData.base_uri" :placeholder="t('wechatBaseUriPlaceholder')" class="input-width" clearable />
<div class="form-tip">{{ t('wechatBaseUriTips') }}</div>
</el-form-item>
</el-card> </el-card>
<el-card class="box-card !border-none mt-[15px]" shadow="never"> <el-card class="box-card !border-none mt-[15px]" shadow="never">
@ -140,7 +145,8 @@ const formData = reactive<Record<string, any>>({
token: '', token: '',
encoding_aes_key: '', encoding_aes_key: '',
encryption_type: 'not_encrypt', encryption_type: 'not_encrypt',
is_authorization: 0 is_authorization: 0,
base_uri: ''
}) })
const formRef = ref<FormInstance>() const formRef = ref<FormInstance>()

View File

@ -77,6 +77,9 @@
<el-switch v-model="diyStore.global.copyright.isShow" /> <el-switch v-model="diyStore.global.copyright.isShow" />
<div class="text-sm text-gray-400">{{ t('此处控制当前页面版权信息是否显示') }}</div> <div class="text-sm text-gray-400">{{ t('此处控制当前页面版权信息是否显示') }}</div>
</el-form-item> </el-form-item>
<el-form-item :label="t('文字颜色')" class="display-block">
<el-color-picker v-model="diyStore.global.copyright.textColor" show-alpha />
</el-form-item>
</el-form> </el-form>
</div> </div>
<div class="edit-attr-item-wrap"> <div class="edit-attr-item-wrap">

View File

@ -455,7 +455,7 @@ initPage({
diyStore.components.push(com) diyStore.components.push(com)
} }
} }
console.log( component.value )
loadDiyTemplatePages(data.type) loadDiyTemplatePages(data.type)
// //

View File

@ -1,47 +1,127 @@
<template> <template>
<!--应用市场--> <!--应用市场-->
<div class="main-container"> <div class="main-container bg-body min-h-[70vh]" v-loading="loading">
<el-card class="box-card !border-none" shadow="never"> <el-card class="box-card !border-none" shadow="never" v-if="!loading">
<div class="flex justify-between items-center"> <div class="flex items-center mb-4">
<span class="text-page-title">{{ t("localAppText") }}</span> <div class="flex items-center flex-1">
<h2 class="text-lg font-semibold mr-[20px]">应用列表</h2>
<button class="text-gray-500 text-sm mr-[20px]" @click="activeNameTabFn('installed')" :class="{'!text-primary': activeName == 'installed'}">已安装</button>
<button class="text-gray-500 text-sm mr-[20px]" @click="activeNameTabFn('uninstalled')" :class="{'!text-primary': activeName == 'uninstalled'}">未安装</button>
<button class="text-gray-500 text-sm mr-[20px]" @click="activeNameTabFn('all')" :class="{'!text-primary': activeName == 'all'}">已购买</button>
<button class="text-gray-500 text-sm relative" @click="activeNameTabFn('recentlyUpdated')" :class="{'!text-primary': activeName == 'recentlyUpdated'}">
可更新
<div v-if="localList.recentlyUpdated.length" class="w-[8px] h-[8px] bg-[red] rounded-[8px] z-1 absolute top-[-2px] right-[-2px]"></div>
</button>
</div>
<i class="iconfont cursor-pointer" @click="switchShowType" :class="showType == 'card' ? 'iconliebiao' : 'iconliebiaoqiehuan'"></i>
</div> </div>
<el-tabs v-model="activeName" class="mt-[10px]"> <!-- 系统版本栏 -->
<el-tab-pane :label="t('installLabel')" name="installed"></el-tab-pane> <div class="flex items-center justify-between bg-body p-4 rounded-md border rounded border-solid border-[var(--el-color-info-light-8)]">
<el-tab-pane :label="t('uninstalledLabel')" name="uninstalled"></el-tab-pane> <div class="flex">
<el-tab-pane :label="t('buyLabel')" name="all"></el-tab-pane> <div class="flex items-center">
<el-tab-pane :label="t('recentlyUpdated')" name="recentlyUpdated"> <div class="w-[40px] h-[40px] bg-purple-100 rounded flex items-center justify-center mr-2 relative">
<template #label> <img class="max-w-full max-h-full" src="@/app/assets/images/app_store/system_version.png" alt="" />
<span class="custom-tabs-label"> <div v-if="frameworkVersion != frameworkNewVersion" class="w-[8px] h-[8px] bg-[red] rounded-[8px] z-1 absolute top-[-2px] right-[-2px]"></div>
<span>{{ t('recentlyUpdated') }}</span>
<span v-if="localList['recentlyUpdated'].length > 0" class="w-[15px] h-[15px] bg-[#DA203E] absolute text-[#fff] text-[11px] flex items-center justify-center rounded-full top-[3px] right-[-12px]">{{ localList['recentlyUpdated'].length }}</span>
</span>
</template>
</el-tab-pane>
</el-tabs>
<div class="flex justify-between my-[10px]">
<div class="flex items-center search-form">
<el-input class="!w-[192px] !h-[32px] rounded-[4px]" :placeholder="t('search')" v-model.trim="search_name" @keyup.enter="query">
<template #suffix>
<el-icon class="el-input__icon cursor-pointer" size="14px" @click="query">
<search />
</el-icon>
</template>
</el-input>
<el-select v-model="search_type" placeholder="请选择类型" class="!w-[192px] !h-[32px] rounded-[4px] ml-[20px] " >
<el-option :label="t('全部')" value="" />
<el-option v-for="(label, value) in typeList" :key="value" :label="label" :value="value"></el-option>
</el-select>
<el-button type="primary" @click="query" class="ml-[20px]">{{ t("搜索") }}</el-button>
</div> </div>
<div> <div>
<el-button type="primary" v-show="activeName === 'recentlyUpdated'" @click="batchUpgrade" :loading="upgradeRef?.loading" :disabled="authLoading">{{ t("batchUpgrade") }}</el-button> <p class="text-sm font-bold">系统版本:
<!-- <el-button type="primary" @click="handleCloudBuild" :loading="cloudBuildRef?.loading" :disabled="authLoading">{{ t("cloudBuild") }}</el-button> --> <span v-if="frameworkVersion">V{{frameworkVersion}} ({{frameworkVersionCode}})</span>
</p>
<p class="text-xs text-gray-500">最新版本: V{{ frameworkNewVersion }}</p>
</div>
<el-button class="ml-[25px]" @click="updateInformationFn({key: 'niucloud-admin'})"><el-icon class="mr-[5px]"><DocumentCopy /></el-icon>更新记录</el-button>
</div>
<div class="border-r mx-[25px] border-solid border-[var(--el-color-info-light-8)] hidden xl:inline-block"></div>
<div class="items-center hidden xl:flex">
<div class="flex items-center mr-4">
<div class="w-[40px] h-[40px] bg-green-100 rounded flex items-center justify-center mr-2">
<img class="max-w-full max-h-full" src="@/app/assets/images/app_store/app_manage.png" alt="" />
</div>
<div>
<p class="text-sm font-bold">应用管理</p>
<p class="text-xs text-gray-500">
<span class="mr-[15px] text-success">已安装: {{ localList.installed.length }}</span>
<span class="mr-[15px] text-warning">可更新: {{ localList.recentlyUpdated.length }}</span>
<span class="mr-[15px] text-primary">未安装: {{ localList.uninstalled.length }}</span>
<span class="mr-[15px] text-error">已购买: {{ localList.all.length }}</span>
</p>
</div>
</div>
</div>
</div>
<div class="flex items-center flex-1 w-0 justify-end">
<el-button @click="oneClickRepair"><i class="iconfont iconyijianxiufu mr-[5px]" ></i>一键修复</el-button>
<el-button type="primary" v-show="activeName == 'uninstalled'" @click="batchInstall"><i class="iconfont iconanzhuang1 mr-[5px]"></i>安装<span v-if="batchUpgradeApp.length">({{ batchUpgradeApp.length }})</span></el-button>
<el-button type="primary" v-show="activeName == 'recentlyUpdated'" @click="batchUpgrade"><i class="iconfont iconyijianshengji mr-[5px]"></i>一键升级<span v-if="batchUpgradeApp.length">({{ batchUpgradeApp.length }})</span></el-button>
<el-checkbox label="全选" :model-value="localList[activeName].length && localList[activeName].length == batchUpgradeApp.length" @change="appKeyAllSelect" v-show="activeName == 'uninstalled' || activeName == 'recentlyUpdated'" value="Value A" class="ml-[12px]"/>
</div>
</div>
</el-card>
<div class="flex mb-4 flex-wrap" v-show="showType == 'card'" v-if="localList[activeName].length && !loading">
<div class="rounded-md border p-[16px] pr-[20px] app-card mb-[20px] ml-[20px] cursor-pointer"
@click="appKeySingleSelect($event, row.key)"
:class="{'border-primary': batchUpgradeApp.includes(row.key)}" v-for="row in localList[activeName]" :key="row.key">
<div class="flex justify-between mb-2">
<div class="flex items-center flex-1 w-0">
<div class="w-[48px] h-[48px] bg-purple-100 rounded flex items-center justify-center mr-2 relative">
<div class="w-full h-full overflow-hidden rounded">
<el-image class="w-full h-full overflow-hidden rounded" :src="row.icon" fit="contain">
<template #error>
<div class="flex items-center w-full h-full">
<img class="max-w-full max-h-full" src="@/app/assets/images/icon-addon-one.png" alt="" />
</div>
</template>
</el-image>
<img src="@/app/assets/images/app_store/app_type_addon.png" alt="" class="absolute z-1 right-0 bottom-0" v-if="row.type == 'addon'">
<img src="@/app/assets/images/app_store/app_type_app.png" alt="" class="absolute z-1 right-0 bottom-0" v-else>
</div>
<div class="w-[8px] h-[8px] bg-[red] rounded-[8px] z-1 absolute top-[-2px] right-[-2px]" v-if="row.install_info && Object.keys(row.install_info)?.length && row.install_info.version != row.version"></div>
</div>
<div class="flex-1 w-0">
<p class="text-sm font-medium truncate" :title="row.title">{{ row.title }}</p>
<p class="text-xs text-gray-500 truncate" :title="row.key">{{ row.key }}</p>
</div>
</div>
<el-checkbox @click.stop :model-value="batchUpgradeApp.includes(row.key)" :value="row.key" @change="appKeySingleSelect($event, row.key)" class="!w-[14px] !h-[14px]" v-if="activeName == 'recentlyUpdated' || activeName == 'uninstalled'"></el-checkbox>
</div>
<div class="flex justify-between">
<div class="text-base">
<span>版本: </span>
<span>{{ row.install_info && Object.keys(row.install_info)?.length ? row.install_info.version : row.version }}</span>
<template v-if="row.install_info && Object.keys(row.install_info)?.length && row.install_info.version != row.version">
<i class="iconfont iconyoujiantou text-warning mx-[2px]"></i>
<span class="text-warning">{{ row.version }}</span>
</template>
</div>
<el-button type="primary" link @click.stop="updateInformationFn(row)">更新记录</el-button>
</div> </div>
<div class="flex mt-[20px]">
<template v-if="!row.is_download">
<el-button type="primary" class="flex-1" :loading="downloading == row.key" :disabled="downloading != ''" @click.stop="downEvent(row)"><i class="iconfont iconanzhuang1 mr-[5px]"></i>立即下载</el-button>
</template>
<template v-else-if="!row.install_info || Object.keys(row.install_info).length == 0">
<el-button type="primary" class="flex-1" @click.stop="installAddonFn(row.key)"><i class="iconfont iconanzhuang1 mr-[5px]"></i>立即安装</el-button>
<el-button plain @click.stop="deleteAddonFn(row.key)">删除</el-button>
</template>
<template v-else>
<el-button type="warning" class="flex-1" @click.stop="upgradeAddonFn(row.key)" v-if="row.install_info.version != row.version">
<i class="iconfont icongengxin mr-[5px]"></i>立即更新
</el-button>
<el-button class="flex-1" :disabled="true" v-else>
<el-icon class="mr-[5px]"><Check /></el-icon>已是最新
</el-button>
<el-button plain @click.stop="uninstallAddonFn(row.key)">卸载</el-button>
</template>
</div> </div>
<div class="relative"> </div>
<el-table v-if="localList[activeName].length && !loading" :tree-props="{ children: 'children' }" :default-expand-all="true" :data="info[activeName]" row-key="key" size="large" @selection-change="handleSelectionChange"> </div>
<div class="relative px-[20px] pb-[20px]" v-show="showType == 'list'">
<el-table v-if="localList[activeName].length && !loading" ref="tableRef" :tree-props="{ children: 'children' }" :default-expand-all="true" :data="info[activeName]" row-key="key" size="large">
<el-table-column width="24"> <el-table-column width="24">
<template #default="{ row }"> <template #default="{ row }">
<div class="tree-child-cell" :class="{ 'is-tree-parent': row.children?.length, 'is-tree-child': typeof row.support_app === 'string' && row.support_app !== '' && visibleRowKeys.has(row.support_app)}"> <div class="tree-child-cell" :class="{ 'is-tree-parent': row.children?.length, 'is-tree-child': typeof row.support_app === 'string' && row.support_app !== '' && visibleRowKeys.has(row.support_app)}">
@ -49,7 +129,11 @@
</div> </div>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column type="selection" v-if="activeName === 'recentlyUpdated'" /> <el-table-column v-if="activeName === 'recentlyUpdated' || activeName === 'uninstalled'" width="60px">
<template #default="{ row }">
<el-checkbox @click.stop :model-value="batchUpgradeApp.includes(row.key)" :value="row.key" @change="appKeySingleSelect($event, row.key)"></el-checkbox>
</template>
</el-table-column>
<el-table-column :label="t('appName')" align="left" width="500"> <el-table-column :label="t('appName')" align="left" width="500">
<template #default="{ row }"> <template #default="{ row }">
<div class="flex items-center cursor-pointer relative left-[-10px]"> <div class="flex items-center cursor-pointer relative left-[-10px]">
@ -139,6 +223,8 @@
</el-table> </el-table>
<div class="h-[100px]" v-loading="loading" v-if="loading"></div> <div class="h-[100px]" v-loading="loading" v-if="loading"></div>
</div> </div>
</div>
<el-empty class="mx-auto overview-empty" v-if="!localList.installed.length && !loading && activeName == 'installed' && !authLoading"> <el-empty class="mx-auto overview-empty" v-if="!localList.installed.length && !loading && activeName == 'installed' && !authLoading">
<template #image> <template #image>
<div class="w-[230px] mx-auto"> <div class="w-[230px] mx-auto">
@ -195,7 +281,7 @@
</p> </p>
</template> </template>
</el-empty> </el-empty>
<el-empty class="mx-auto overview-empty" v-if="!localList.recentlyUpdated.length && !loading && authinfo && activeName == 'recentlyUpdated' && !authLoading"> <el-empty class="mx-auto overview-empty" v-if="!localList.recentlyUpdated.length && !loading && activeName == 'recentlyUpdated'">
<template #image> <template #image>
<div class="w-[230px] mx-auto"> <div class="w-[230px] mx-auto">
<img src="@/app/assets/images/index/apply_empty.png" class="max-w-full" alt="" /> <img src="@/app/assets/images/index/apply_empty.png" class="max-w-full" alt="" />
@ -205,7 +291,6 @@
<p class="flex items-center">{{ t("recentlyUpdatedEmpty") }}</p> <p class="flex items-center">{{ t("recentlyUpdatedEmpty") }}</p>
</template> </template>
</el-empty> </el-empty>
</div>
<el-dialog v-model="authCodeApproveDialog" title="授权码认证" width="400px"> <el-dialog v-model="authCodeApproveDialog" title="授权码认证" width="400px">
<el-form :model="formData" label-width="0" ref="formRef" :rules="formRules" class="page-form"> <el-form :model="formData" label-width="0" ref="formRef" :rules="formRules" class="page-form">
@ -265,9 +350,10 @@
<div v-show="installStep == 0" v-loading="!installCheckResult.dir"> <div v-show="installStep == 0" v-loading="!installCheckResult.dir">
<!-- <el-scrollbar max-height="50vh"> --> <!-- <el-scrollbar max-height="50vh"> -->
<div class="min-h-[150px]"> <div class="min-h-[150px]">
<div class="my-3" v-if="installCheckResult.dir"> <el-scrollbar style="height: calc(50vh); overflow: auto">
<div class="mt-3" v-if="installCheckResult.dir">
<p class="pt-[20px] pl-[20px]">{{ t("dirPermission") }}</p> <p class="pt-[20px] pl-[20px]">{{ t("dirPermission") }}</p>
<div v-if="!installCheckResult.is_pass" class="mt-[10px] mx-[20px] text-[14px] cursor-pointer text-primary flex items-center justify-between bg-[#EFF6FF] rounded-[4px] p-[10px]" @click="cloudBuildCheckDirFn"> <div v-if="!installCheckResult.file_permission_is_pass" class="mt-[10px] mx-[20px] text-[14px] cursor-pointer text-primary flex items-center justify-between bg-[#EFF6FF] rounded-[4px] p-[10px]" @click="cloudBuildCheckDirFn">
<div class="flex items-center"> <div class="flex items-center">
<el-icon :size="17"><QuestionFilled /></el-icon> <el-icon :size="17"><QuestionFilled /></el-icon>
<span class="ml-[5px] leading-[20px]">编译权限错误查看解决方案</span></div> <span class="ml-[5px] leading-[20px]">编译权限错误查看解决方案</span></div>
@ -285,7 +371,6 @@
<span>{{ t("status") }}</span> <span>{{ t("status") }}</span>
</el-col> </el-col>
</el-row> </el-row>
<el-scrollbar style="height: calc(300px); overflow: auto">
<el-row class="pb-[10px] items pl-[15px]" v-for="(item, index) in installCheckResult.dir.is_readable" :key="index"> <el-row class="pb-[10px] items pl-[15px]" v-for="(item, index) in installCheckResult.dir.is_readable" :key="index">
<el-col :span="18"> <el-col :span="18">
<span>{{ item.dir }}</span> <span>{{ item.dir }}</span>
@ -326,10 +411,18 @@
</span> </span>
</el-col> </el-col>
</el-row> </el-row>
</div>
</div>
<div class="my-3" v-if="installCheckResult.addon_check && installCheckResult.addon_check.length">
<p class="pl-[20px]">插件验证</p>
<div class="px-[20px] pt-[10px] text-[14px]">
<el-alert class="!mb-[10px]" v-for="item in installCheckResult.addon_check" type="error" :closable="false">
<div v-html="item.msg"></div>
</el-alert>
</div>
</div>
</el-scrollbar> </el-scrollbar>
</div> </div>
</div>
</div>
<!-- </el-scrollbar> --> <!-- </el-scrollbar> -->
<div class="flex justify-end"> <div class="flex justify-end">
<el-tooltip effect="dark" placement="top"> <el-tooltip effect="dark" placement="top">
@ -465,7 +558,6 @@
</template> </template>
</el-dialog> </el-dialog>
<!-- 更新信息 --> <!-- 更新信息 -->
</el-card>
</div> </div>
<upgrade-log :upgradeKey="upgradeKey" ref="upgradeLogRef" /> <upgrade-log :upgradeKey="upgradeKey" ref="upgradeLogRef" />
<upgrade ref="upgradeRef" @complete="localListFn" @cloudbuild="handleCloudBuild" /> <upgrade ref="upgradeRef" @complete="localListFn" @cloudbuild="handleCloudBuild" />
@ -473,7 +565,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref, reactive, watch, h ,computed } from 'vue' import { ref, reactive, watch, h, computed, nextTick } from 'vue'
import { t } from '@/lang' import { t } from '@/lang'
import { import {
getAddonLocal, getAddonLocal,
@ -488,7 +580,7 @@ import {
getAddonInit getAddonInit
} from '@/app/api/addon' } from '@/app/api/addon'
import { deleteAddonDevelop } from '@/app/api/tools' import { deleteAddonDevelop } from '@/app/api/tools'
import { downloadVersion, getAuthInfo, setAuthInfo } from '@/app/api/module' import { downloadVersion, getAuthInfo, setAuthInfo, getFrameworkNewVersion } from '@/app/api/module'
import { getVersions } from '@/app/api/auth' import { getVersions } from '@/app/api/auth'
import { ElMessage, ElMessageBox, ElNotification, FormInstance, FormRules } from 'element-plus' import { ElMessage, ElMessageBox, ElNotification, FormInstance, FormRules } from 'element-plus'
import 'vue-web-terminal/lib/theme/dark.css' import 'vue-web-terminal/lib/theme/dark.css'
@ -501,9 +593,10 @@ import Upgrade from '@/app/components/upgrade/index.vue'
import CloudBuild from '@/app/components/cloud-build/index.vue' import CloudBuild from '@/app/components/cloud-build/index.vue'
import UpgradeLog from '@/app/components/upgrade-log/index.vue' import UpgradeLog from '@/app/components/upgrade-log/index.vue'
const tableRef = ref(null)
const router = useRouter() const router = useRouter()
const route = useRoute() const route = useRoute()
const terminalId = ref(Date.now()); const terminalId = ref(Date.now())
const activeName = ref(storage.get('storeActiveName') || 'installed') const activeName = ref(storage.get('storeActiveName') || 'installed')
const upgradeRef = ref(null) const upgradeRef = ref(null)
const cloudBuildRef = ref(null) const cloudBuildRef = ref(null)
@ -514,10 +607,22 @@ const userStore = useUserStore()
const unloadHintDialog = ref(false) const unloadHintDialog = ref(false)
const terminalRef = ref(null) const terminalRef = ref(null)
const frameworkVersion = ref('') const frameworkVersion = ref('')
const frameworkVersionCode = ref('')
const upgradeLogRef = ref<any>(null) const upgradeLogRef = ref<any>(null)
const showType = ref(storage.get('storeShowType') || 'card')
const frameworkNewVersion = ref('')
getVersions().then((res) => { getVersions().then((res) => {
frameworkVersion.value = res.data.version.version frameworkVersion.value = res.data.version.version
frameworkVersionCode.value = res.data.version.code
}) })
getFrameworkNewVersion().then(({ data }) => {
frameworkNewVersion.value = data.last_version
})
const switchShowType = () => {
showType.value = showType.value == 'card' ? 'list' : 'card'
storage.set({key: 'storeShowType', data: showType.value})
}
const typeList = ref({}) const typeList = ref({})
const getAddonInitFn = () => { const getAddonInitFn = () => {
@ -534,6 +639,12 @@ const downEventHintFn = () => {
const activeNameTabFn = (data: any) => { const activeNameTabFn = (data: any) => {
activeName.value = data activeName.value = data
storage.set({ key: 'storeActiveName', data }) storage.set({ key: 'storeActiveName', data })
if ((data == 'uninstalled' || data == 'recentlyUpdated') && localList.value[activeName.value].length) {
batchUpgradeApp.value = localList.value[activeName.value].map(item => item.key)
} else {
batchUpgradeApp.value = []
}
} }
if (route.query.id) { if (route.query.id) {
activeNameTabFn(route.query.id) activeNameTabFn(route.query.id)
@ -603,26 +714,7 @@ const buildInfo = (list: any[]) => {
return result return result
} }
// const query = () => {
// if (search_name.value == '' || search_name.value == null) {
// info.value.installed = buildInfo(localList.value.installed)
// info.value.uninstalled = buildInfo(localList.value.uninstalled)
// info.value.all = buildInfo(localList.value.all)
// info.value.recentlyUpdated = buildInfo(localList.value.recentlyUpdated)
// return false
// }
// const filteredInstalled = localList.value.installed.filter((el: any) => el.title.indexOf(search_name.value) != -1)
// const filteredUninstalled = localList.value.uninstalled.filter((el: any) => el.title.indexOf(search_name.value) != -1)
// const filteredAll = localList.value.all.filter((el: any) => el.title.indexOf(search_name.value) != -1)
// const filteredRecentlyUpdated = localList.value.recentlyUpdated.filter((el: any) => el.title.indexOf(search_name.value) != -1)
// //
// info.value.installed = buildInfo(filteredInstalled)
// info.value.uninstalled = buildInfo(filteredUninstalled)
// info.value.all = buildInfo(filteredAll)
// info.value.recentlyUpdated = buildInfo(filteredRecentlyUpdated)
// }
const query = () => { const query = () => {
const name = search_name.value const name = search_name.value
const type = search_type.value const type = search_type.value
@ -689,6 +781,7 @@ const localListFn = () => {
appLink.value[item.meta.app] = item.name appLink.value[item.meta.app] = item.name
} }
}) })
activeNameTabFn(activeName.value)
loading.value = false loading.value = false
}).catch(() => { }).catch(() => {
loading.value = false loading.value = false
@ -909,6 +1002,7 @@ const handleCloudInstall = () => {
installStep.value = 1 installStep.value = 1
terminalRef.value.execute('clear') terminalRef.value.execute('clear')
terminalRef.value.execute('开始安装插件') terminalRef.value.execute('开始安装插件')
if (res.data.length) installAfterTips.value = res.data
getInstallTask() getInstallTask()
cloudInstalling.value = false cloudInstalling.value = false
}).catch((res) => { }).catch((res) => {
@ -1155,23 +1249,55 @@ const versionJudge = (row: any) => {
return false return false
} }
let batchUpgradeApp = [] const batchUpgradeApp = ref<String[]>([])
const handleSelectionChange = (e: any) => {
batchUpgradeApp = e.map(item => item.key) const appKeyAllSelect = () => {
if (localList.value[activeName.value].length) {
if (localList.value[activeName.value].length == batchUpgradeApp.value.length) {
batchUpgradeApp.value = []
} else {
batchUpgradeApp.value = localList.value[activeName.value].map(item => item.key)
}
}
}
const appKeySingleSelect = (event: any, key: string) => {
if (activeName.value != 'recentlyUpdated' && activeName.value != 'uninstalled') return
if (batchUpgradeApp.value.includes(key)) {
batchUpgradeApp.value.splice(batchUpgradeApp.value.indexOf(key), 1)
} else {
batchUpgradeApp.value.push(key)
}
} }
const batchUpgrade = () => { const batchUpgrade = () => {
if (!batchUpgradeApp.length) { const appKeys = batchUpgradeApp.value
ElMessage({ message: '请先勾选要升级的插件', type: 'error', duration: 5000 }) if (frameworkVersion.value != frameworkNewVersion.value) {
appKeys.unshift('niucloud-admin')
}
if (!appKeys.length) {
ElMessage({ message: localList.recentlyUpdated.length ? '请先勾选要升级的插件' : '当前已是最新版', type: 'error', duration: 5000 })
return return
} }
upgradeAddonFn(appKeys.toString())
}
upgradeAddonFn(batchUpgradeApp.toString()) const batchInstall = () => {
const appKeys = batchUpgradeApp.value
if (!appKeys.length) {
ElMessage({ message: '请先勾选要安装的插件', type: 'error', duration: 5000 })
return
}
installAddonFn(appKeys.toString())
} }
const visibleRowKeys = computed(() => { const visibleRowKeys = computed(() => {
return new Set((info.value[activeName.value] || []).map(row => row.key)); return new Set((info.value[activeName.value] || []).map(row => row.key))
}); })
const oneClickRepair = () => {
ElMessage({ message: '即将上线敬请期待', duration: 5000 })
}
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@ -1451,6 +1577,11 @@ html.dark .table-head-bg {
} }
} }
.app-card {
width: calc((100% - 120px) / 5);
min-width: 260px;
}
</style> </style>
<style> <style>

View File

@ -126,7 +126,9 @@ const prop = defineProps({
}, },
ignore: { ignore: {
type: Array, type: Array,
default: [] default: () => {
return [] // ['DIY_MAKE_PHONE_CALL']
}
} }
}) })

View File

@ -82,6 +82,7 @@ getUrl().then((res: any) => {
// H5 // H5
const generateH5QRCode = () => { const generateH5QRCode = () => {
console.log( params.value)
// URL // URL
const queryStr = params.value const queryStr = params.value
.map(item => `${encodeURIComponent(item.name)}=${encodeURIComponent(item.value)}`) .map(item => `${encodeURIComponent(item.name)}=${encodeURIComponent(item.value)}`)

View File

@ -219,5 +219,10 @@
"cloudBuildTips": "是否要进行云编译该操作可能会影响到正在访问的客户是否要继续操作?", "cloudBuildTips": "是否要进行云编译该操作可能会影响到正在访问的客户是否要继续操作?",
"promoteUrl": "推广链接", "promoteUrl": "推广链接",
"downLoadQRCode": "下载二维码", "downLoadQRCode": "下载二维码",
"configureFailed": "配置失败" "configureFailed": "配置失败",
"lefttitle": "左侧标题",
"righttitle": "右侧标题",
"leftDesc": "左侧简介",
"rightDesc": "右侧简介",
"descPlaceholder": "请输入简介内容"
} }

View File

@ -17,10 +17,10 @@
<div v-if="systemStore.menuIsCollapse" class="text-left text-[14px] mt-[3px] w-[75px] using-hidden ml-[10px]">{{ item.meta.title || item.meta.short_title }}</div> <div v-if="systemStore.menuIsCollapse" class="text-left text-[14px] mt-[3px] w-[75px] using-hidden ml-[10px]">{{ item.meta.title || item.meta.short_title }}</div>
<div v-else class="text-center text-[12px] using-hidden mt-1">{{ item.meta.short_title || item.meta.title }}</div> <div v-else class="text-center text-[12px] using-hidden mt-1">{{ item.meta.short_title || item.meta.title }}</div>
<div v-if="systemStore.menuIsCollapse && item.name=='app_store' && recentlyUpdated.length>0" class="text-[11px] bg-[#DA203E] px-[10px] rounded-[12px] text-[#fff] absolute right-[6px]">更新</div> <div v-if="systemStore.menuIsCollapse && item.name=='app_store' && (recentlyUpdated.length>0 || isNewVersion)" class="text-[11px] bg-[#DA203E] px-[10px] rounded-[12px] text-[#fff] absolute right-[6px]">更新</div>
<div v-if="!systemStore.menuIsCollapse && item.name=='app_store' && recentlyUpdated.length>0" class="w-[7px] h-[7px] bg-[#DA203E] absolute flex items-center justify-center rounded-full top-[4px] right-[14px]"></div> <div v-if="!systemStore.menuIsCollapse && item.name=='app_store' && (recentlyUpdated.length>0 || isNewVersion)" class="w-[7px] h-[7px] bg-[#DA203E] absolute flex items-center justify-center rounded-full top-[4px] right-[14px]"></div>
<div v-if="systemStore.menuIsCollapse && item.original_name=='tool' && isNewVersion" class="text-[11px] bg-[#DA203E] px-[10px] rounded-[12px] text-[#fff] absolute right-[6px]">更新</div> <!-- <div v-if="systemStore.menuIsCollapse && item.original_name=='tool' && isNewVersion" class="text-[11px] bg-[#DA203E] px-[10px] rounded-[12px] text-[#fff] absolute right-[6px]">更新</div>
<div v-if="!systemStore.menuIsCollapse && item.original_name=='tool' && isNewVersion" class="w-[7px] h-[7px] bg-[#DA203E] absolute flex items-center justify-center rounded-full top-[4px] right-[14px]"></div> <div v-if="!systemStore.menuIsCollapse && item.original_name=='tool' && isNewVersion" class="w-[7px] h-[7px] bg-[#DA203E] absolute flex items-center justify-center rounded-full top-[4px] right-[14px]"></div> -->
</div> </div>
</template> </template>

View File

@ -87,6 +87,7 @@ const useDiyStore = defineStore('diy', {
copyright: { copyright: {
control: true, // 是否允许展示编辑 control: true, // 是否允许展示编辑
isShow: false, // 是否显示 isShow: false, // 是否显示
textColor : "#ccc", // 文字颜色
}, },
// 弹框 count不弹出 -1首次弹出 1每次弹出 0 // 弹框 count不弹出 -1首次弹出 1每次弹出 0
popWindow: { popWindow: {
@ -190,6 +191,7 @@ const useDiyStore = defineStore('diy', {
copyright: { copyright: {
control: true, // 是否允许展示编辑 control: true, // 是否允许展示编辑
isShow: true, // 是否显示 isShow: true, // 是否显示
textColor : "#ccc", // 文字颜色
}, },
// 弹框 count不弹出 -1首次弹出 1每次弹出 0 // 弹框 count不弹出 -1首次弹出 1每次弹出 0

View File

@ -1,9 +1,9 @@
@font-face { @font-face {
font-family: "iconfont"; font-family: "iconfont";
/* Project id 3883393 */ /* Project id 3883393 */
src: url('//at.alicdn.com/t/c/font_3883393_6d60cyygl4.woff2?t=1755603992297') format('woff2'), src: url('//at.alicdn.com/t/c/font_3883393_0604cbkh5j03.woff2?t=1762651161569') format('woff2'),
url('//at.alicdn.com/t/c/font_3883393_6d60cyygl4.woff?t=1755603992297') format('woff'), url('//at.alicdn.com/t/c/font_3883393_0604cbkh5j03.woff?t=1762651161569') format('woff'),
url('//at.alicdn.com/t/c/font_3883393_6d60cyygl4.ttf?t=1755603992297') format('truetype'); url('//at.alicdn.com/t/c/font_3883393_0604cbkh5j03.ttf?t=1762651161569') format('truetype');
} }
.iconfont { .iconfont {
@ -14,6 +14,78 @@
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
} }
.icona-zhulixiangqingpc30:before {
content: "\e914";
}
.icona-zuidijiapc30:before {
content: "\e915";
}
.icona-zhulipc30:before {
content: "\e916";
}
.icona-zhulijiapc30:before {
content: "\e917";
}
.icona-kanhoujiapc30:before {
content: "\e918";
}
.icona-jiagepc30:before {
content: "\e919";
}
.icona-zhuliwanfapc30:before {
content: "\e91a";
}
.icona-zhulipc301:before {
content: "\e91b";
}
.icona-zhulilunbopc30:before {
content: "\e91c";
}
.icona-canyuxinxipc30:before {
content: "\e91d";
}
.icona-zhulixiangqingpc301:before {
content: "\e91e";
}
.iconyoujiantou:before {
content: "\e913";
}
.iconanzhuang1:before {
content: "\e90d";
}
.icongengxin:before {
content: "\e90e";
}
.iconliebiao:before {
content: "\e90f";
}
.iconyijianshengji:before {
content: "\e910";
}
.iconliebiaoqiehuan:before {
content: "\e911";
}
.iconyijianxiufu:before {
content: "\e912";
}
.icona-bijiPC30:before { .icona-bijiPC30:before {
content: "\e90b"; content: "\e90b";
} }

View File

@ -131,9 +131,10 @@ export function img(path: string): string {
if (typeof path == 'string' && path.startsWith('/')) path = path.replace(/^\//, '') if (typeof path == 'string' && path.startsWith('/')) path = path.replace(/^\//, '')
if (typeof imgDomain == 'string' && imgDomain.endsWith('/')) imgDomain = imgDomain.slice(0, -1) if (typeof imgDomain == 'string' && imgDomain.endsWith('/')) imgDomain = imgDomain.slice(0, -1)
if(path){
return isUrl(path) ? path : `${imgDomain}/${path}` return isUrl(path) ? path : `${imgDomain}/${path}`
} }
}
/** /**
* asset img * asset img