提交 93965ea6 authored 作者: 王鹏飞's avatar 王鹏飞

updates

上级 c1e259c9
......@@ -5,6 +5,7 @@
<link rel="icon" href="https://zws-imgs-pub.ezijing.com/pc/base/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>PRP私享星球管理系统</title>
<script src="https://webapp-pub.ezijing.com/plugins/tinymce@6/tinymce.min.js"></script>
</head>
<body>
<div id="app"></div>
......
......@@ -14,20 +14,20 @@
},
"dependencies": {
"@element-plus/icons-vue": "^1.1.4",
"@tinymce/tinymce-vue": "^5.0.0",
"axios": "^0.26.1",
"blueimp-md5": "^2.19.0",
"element-plus": "^2.1.10",
"pinia": "^2.0.13",
"qs": "^6.10.3",
"sass": "^1.50.0",
"sass": "^1.50.1",
"vue": "^3.2.33",
"vue-router": "^4.0.14",
"vuedraggable": "^4.1.0"
"vue-router": "^4.0.14"
},
"devDependencies": {
"@rushstack/eslint-patch": "^1.1.3",
"@types/blueimp-md5": "^2.18.0",
"@types/node": "^17.0.24",
"@types/node": "^17.0.25",
"@types/qs": "^6.9.7",
"@vitejs/plugin-vue": "^2.3.1",
"@vue/eslint-config-typescript": "^10.0.0",
......@@ -39,6 +39,6 @@
"typescript": "~4.6.3",
"vite": "^2.9.5",
"vite-plugin-checker": "^0.4.6",
"vue-tsc": "^0.34.7"
"vue-tsc": "^0.34.9"
}
}
......@@ -3,8 +3,9 @@ lockfileVersion: 5.3
specifiers:
'@element-plus/icons-vue': ^1.1.4
'@rushstack/eslint-patch': ^1.1.3
'@tinymce/tinymce-vue': ^5.0.0
'@types/blueimp-md5': ^2.18.0
'@types/node': ^17.0.24
'@types/node': ^17.0.25
'@types/qs': ^6.9.7
'@vitejs/plugin-vue': ^2.3.1
'@vue/eslint-config-typescript': ^10.0.0
......@@ -18,43 +19,42 @@ specifiers:
eslint-plugin-vue: ^8.6.0
pinia: ^2.0.13
qs: ^6.10.3
sass: ^1.50.0
sass: ^1.50.1
typescript: ~4.6.3
vite: ^2.9.5
vite-plugin-checker: ^0.4.6
vue: ^3.2.33
vue-router: ^4.0.14
vue-tsc: ^0.34.7
vuedraggable: ^4.1.0
vue-tsc: ^0.34.9
dependencies:
'@element-plus/icons-vue': 1.1.4_vue@3.2.33
'@tinymce/tinymce-vue': 5.0.0_vue@3.2.33
axios: 0.26.1
blueimp-md5: 2.19.0
element-plus: 2.1.10_vue@3.2.33
pinia: 2.0.13_typescript@4.6.3+vue@3.2.33
qs: 6.10.3
sass: 1.50.0
sass: 1.50.1
vue: 3.2.33
vue-router: 4.0.14_vue@3.2.33
vuedraggable: 4.1.0_vue@3.2.33
devDependencies:
'@rushstack/eslint-patch': 1.1.3
'@types/blueimp-md5': 2.18.0
'@types/node': 17.0.24
'@types/node': 17.0.25
'@types/qs': 6.9.7
'@vitejs/plugin-vue': 2.3.1_vite@2.9.5+vue@3.2.33
'@vue/eslint-config-typescript': 10.0.0_a62cbc2f4797496d74696b1f6538012a
'@vue/tsconfig': 0.1.3_@types+node@17.0.24
'@vue/tsconfig': 0.1.3_@types+node@17.0.25
ali-oss: 6.17.1
chalk: 5.0.1
eslint: 8.13.0
eslint-plugin-vue: 8.6.0_eslint@8.13.0
typescript: 4.6.3
vite: 2.9.5_sass@1.50.0
vite: 2.9.5_sass@1.50.1
vite-plugin-checker: 0.4.6_vite@2.9.5
vue-tsc: 0.34.7_typescript@4.6.3
vue-tsc: 0.34.9_typescript@4.6.3
packages:
......@@ -168,6 +168,15 @@ packages:
resolution: {integrity: sha512-WiBSI6JBIhC6LRIsB2Kwh8DsGTlbBU+mLRxJmAe3LjHTdkDpwIbEOZgoXBbZilk/vlfjK8i6nKRAvIRn1XaIMw==}
dev: true
/@tinymce/tinymce-vue/5.0.0_vue@3.2.33:
resolution: {integrity: sha512-1HRCNa2eGdztOKShYAiHIry50LTU6YJG//qSP9AJElrXhb3BIBN0Bef6E56nZObVgwCgSmI4cnX35VU9D49aow==}
peerDependencies:
vue: ^3.0.0
dependencies:
tinymce: 6.0.1
vue: 3.2.33
dev: false
/@tootallnate/once/1.1.2:
resolution: {integrity: sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==}
engines: {node: '>= 6'}
......@@ -191,8 +200,8 @@ packages:
resolution: {integrity: sha512-n3tyKthHJbkiWhDZs3DkhkCzt2MexYHXlX0td5iMplyfwketaOeKboEVBqzceH7juqvEg3q5oUoBFxSLu7zFag==}
dev: false
/@types/node/17.0.24:
resolution: {integrity: sha512-aveCYRQbgTH9Pssp1voEP7HiuWlD2jW2BO56w+bVrJn04i61yh6mRfoKO6hEYQD9vF+W8Chkwc6j1M36uPkx4g==}
/@types/node/17.0.25:
resolution: {integrity: sha512-wANk6fBrUwdpY4isjWrKTufkrXdu1D2YHCot2fD/DfWxF5sMrVSA+KN7ydckvaTCh0HiqX9IVl0L5/ZoXg5M7w==}
dev: true
/@types/qs/6.9.7:
......@@ -332,36 +341,36 @@ packages:
vite: ^2.5.10
vue: ^3.2.25
dependencies:
vite: 2.9.5_sass@1.50.0
vite: 2.9.5_sass@1.50.1
vue: 3.2.33
dev: true
/@volar/code-gen/0.34.7:
resolution: {integrity: sha512-E1N1VGlChXd0D7WPmmjKhtcZdUKNpBFC4BRqfY+7FZGh89FZlw3uG6Nn76/DjMBLVhfCIY9vA8pwWIN1lI8nYw==}
/@volar/code-gen/0.34.9:
resolution: {integrity: sha512-LHkuA4VfjeMjouMyE5LkQyr/q7BBBL+RnpV+4xLN5ad2fW639vUlJKK8JIx6DswMlmLSF88N5s4WFGFmLcl40g==}
dependencies:
'@volar/source-map': 0.34.7
'@volar/source-map': 0.34.9
dev: true
/@volar/source-map/0.34.7:
resolution: {integrity: sha512-KBNcKCWKsY2f965xuuT4dSbt8GR6nHMzb9gi7ucUHtmRQnvrB31BLBvZNQTHMqkbhRmKArDSuIrbUUG9yu0OXQ==}
/@volar/source-map/0.34.9:
resolution: {integrity: sha512-+n1hb6SPKV6Y/MNDYAO9uwB+oSDmoVlFVuYrb6QiZnLowqjB9WSoDg31GRHGNjfepjTruOiAkKa3bLMc0rFsOg==}
dev: true
/@volar/vue-code-gen/0.34.7:
resolution: {integrity: sha512-vejzO30QrDAEZKguZI8hlAnKhwNoX1INXrOMurlmwCbNft2oEloT+ikFF8QYDz3vWWrdFSsoOKp3BTHyurJ5Nw==}
/@volar/vue-code-gen/0.34.9:
resolution: {integrity: sha512-84XExk3rUU59QbJxkQq6xkHb6a3kXBNNeYSGS1+H+GMqx4z3BYa9MnQ0KvfUEM1Nnjyn+IA8RmbuQJMR7POlNw==}
dependencies:
'@volar/code-gen': 0.34.7
'@volar/source-map': 0.34.7
'@volar/code-gen': 0.34.9
'@volar/source-map': 0.34.9
'@vue/compiler-core': 3.2.33
'@vue/compiler-dom': 3.2.33
'@vue/shared': 3.2.33
dev: true
/@volar/vue-typescript/0.34.7:
resolution: {integrity: sha512-Ebln64LQutjuNs8nk57oFo45JMQVdZKThkNAeFrzaqB0UItazRQpSXet4vHzfV18FMCV3Cc6fEqZ14WZzQAxgQ==}
/@volar/vue-typescript/0.34.9:
resolution: {integrity: sha512-+SqUBcXkccrYJlCQAZ5fn9SA9v9pV2fpMblsG44/GTVEopQmgUU2PMby/9RsavEcbl0Byyrv2627Sl5ILC318A==}
dependencies:
'@volar/code-gen': 0.34.7
'@volar/source-map': 0.34.7
'@volar/vue-code-gen': 0.34.7
'@volar/code-gen': 0.34.9
'@volar/source-map': 0.34.9
'@volar/vue-code-gen': 0.34.9
'@vue/compiler-sfc': 3.2.33
'@vue/reactivity': 3.2.33
dev: true
......@@ -463,7 +472,7 @@ packages:
/@vue/shared/3.2.33:
resolution: {integrity: sha512-UBc1Pg1T3yZ97vsA2ueER0F6GbJebLHYlEi4ou1H5YL4KWvMOOWwpYo9/QpWq93wxKG6Wo13IY74Hcn/f7c7Bg==}
/@vue/tsconfig/0.1.3_@types+node@17.0.24:
/@vue/tsconfig/0.1.3_@types+node@17.0.25:
resolution: {integrity: sha512-kQVsh8yyWPvHpb8gIc9l/HIDiiVUy1amynLNpCy8p+FoCiZXCo6fQos5/097MmnNZc9AtseDsCrfkhqCrJ8Olg==}
peerDependencies:
'@types/node': '*'
......@@ -471,7 +480,7 @@ packages:
'@types/node':
optional: true
dependencies:
'@types/node': 17.0.24
'@types/node': 17.0.25
dev: true
/@vueuse/core/8.2.6_vue@3.2.33:
......@@ -2155,8 +2164,8 @@ packages:
resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==}
dev: true
/sass/1.50.0:
resolution: {integrity: sha512-cLsD6MEZ5URXHStxApajEh7gW189kkjn4Rc8DQweMyF+o5HF5nfEz8QYLMlPsTOD88DknatTmBWkOcw5/LnJLQ==}
/sass/1.50.1:
resolution: {integrity: sha512-noTnY41KnlW2A9P8sdwESpDmo+KBNkukI1i8+hOK3footBUcohNHtdOJbckp46XO95nuvcHDDZ+4tmOnpK3hjw==}
engines: {node: '>=12.0.0'}
hasBin: true
dependencies:
......@@ -2240,10 +2249,6 @@ packages:
smart-buffer: 4.2.0
dev: true
/sortablejs/1.14.0:
resolution: {integrity: sha512-pBXvQCs5/33fdN1/39pPL0NZF20LeRbLQ5jtnheIPN9JQAaufGjKdWduZn4U7wCtVuzKhmRkI0DFYHYRbB2H1w==}
dev: false
/source-map-js/1.0.2:
resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==}
engines: {node: '>=0.10.0'}
......@@ -2346,6 +2351,10 @@ packages:
resolution: {integrity: sha512-1Uhn/aqw5C6RI4KejVeTg6mIS7IqxnLJ8Mv2tV5rTc0qWobay7pDUz6Wi392Cnc8ak1H0F2cjoRzb2/AW4+Fvg==}
dev: true
/tinymce/6.0.1:
resolution: {integrity: sha512-+y3Lhm65GFs87gUfKuSWm2JA6ULHCC29trof4umIk2+TT+ql2poSsqsmRCSPN4elEo4EssIgEPFVRyPvrS/pDg==}
dev: false
/to-arraybuffer/1.0.1:
resolution: {integrity: sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=}
dev: true
......@@ -2498,14 +2507,14 @@ packages:
npm-run-path: 4.0.1
strip-ansi: 6.0.1
tiny-invariant: 1.2.0
vite: 2.9.5_sass@1.50.0
vite: 2.9.5_sass@1.50.1
vscode-languageclient: 7.0.0
vscode-languageserver: 7.0.0
vscode-languageserver-textdocument: 1.0.4
vscode-uri: 3.0.3
dev: true
/vite/2.9.5_sass@1.50.0:
/vite/2.9.5_sass@1.50.1:
resolution: {integrity: sha512-dvMN64X2YEQgSXF1lYabKXw3BbN6e+BL67+P3Vy4MacnY+UzT1AfkHiioFSi9+uiDUiaDy7Ax/LQqivk6orilg==}
engines: {node: '>=12.2.0'}
hasBin: true
......@@ -2525,7 +2534,7 @@ packages:
postcss: 8.4.12
resolve: 1.22.0
rollup: 2.70.2
sass: 1.50.0
sass: 1.50.1
optionalDependencies:
fsevents: 2.3.2
dev: true
......@@ -2621,13 +2630,13 @@ packages:
vue: 3.2.33
dev: false
/vue-tsc/0.34.7_typescript@4.6.3:
resolution: {integrity: sha512-GcdwGuddEakVBHKw7uiZUfHqobGD4Ym2XExGuwYuxw7rT50ZnRZvYQ3IB7zyPLa7UZEmiy6HTJiTrArw7ZOu+w==}
/vue-tsc/0.34.9_typescript@4.6.3:
resolution: {integrity: sha512-uR4KIbhDqg5xAEenePfARq7rRgOKp8FJwe0HM1EUYfRLc0ZYZ97dr9WJ8yFlxzZEmfWYm/doWyBK5a5mvom9eg==}
hasBin: true
peerDependencies:
typescript: '*'
dependencies:
'@volar/vue-typescript': 0.34.7
'@volar/vue-typescript': 0.34.9
typescript: 4.6.3
dev: true
......@@ -2641,15 +2650,6 @@ packages:
'@vue/shared': 3.2.33
dev: false
/vuedraggable/4.1.0_vue@3.2.33:
resolution: {integrity: sha512-FU5HCWBmsf20GpP3eudURW3WdWTKIbEIQxh9/8GE806hydR9qZqRRxRE3RjqX7PkuLuMQG/A7n3cfj9rCEchww==}
peerDependencies:
vue: ^3.0.1
dependencies:
sortablejs: 1.14.0
vue: 3.2.33
dev: false
/which/2.0.2:
resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
engines: {node: '>= 8'}
......
......@@ -27,7 +27,5 @@ export function uploadFile(data: Record<string, any>) {
withCredentials: false,
headers: { 'Content-Type': 'multipart/form-data' }
})
.then(() => {
return data.url
})
.then(() => data)
}
......@@ -23,7 +23,7 @@ const props = withDefaults(
hasPagination?: boolean
limit?: number
}>(),
{ hasPagination: true, limit: 20 }
{ hasPagination: true, limit: 10 }
)
const filterFormRef = ref()
......@@ -32,7 +32,7 @@ const tableRef = ref()
const dataList = ref([])
const page = reactive({ total: 0, size: props.limit, currentPage: 1 })
const params = reactive({ page: page.currentPage, limit: page.size, ...props.remote?.params })
const params = reactive({ page: page.currentPage, page_size: page.size, ...props.remote?.params })
// 获取数据
const fetchList = (isReset = false) => {
......@@ -115,7 +115,7 @@ defineExpose({ refetch })
<div class="table-list">
<div class="table-list-hd">
<!-- 筛选 -->
<div class="table-list-filter" v-if="filters.length">
<div class="table-list-filter" v-if="filters && filters.length">
<el-form :inline="true" :model="params" ref="filterFormRef" @submit.prevent>
<template v-for="item in filters" :key="item.prop">
<el-form-item :label="item.label" :prop="item.prop">
......@@ -173,7 +173,7 @@ defineExpose({ refetch })
<el-pagination
class="table-list-pagination"
background
layout="total, prev, pager, next, sizes, jumper"
layout="total, sizes, prev, pager, next, jumper"
:page-sizes="[10, 20, 30, 50, 100]"
:page-size="page.size"
:total="page.total"
......@@ -188,149 +188,6 @@ defineExpose({ refetch })
</div>
</template>
<!-- <script>
export default {
name: 'AppList',
props: {
// 接口请求
remote: { type: Object, default: () => ({}) },
// 筛选
filters: { type: Array, default: () => [] },
// 更多筛选
moreFilters: { type: Array, default: () => [] },
// 列表项
columns: { type: Array, default: () => [] },
// 列表数据
data: { type: Array, default: () => [] },
// 是否含有翻页
hasPagination: { type: Boolean, default: true },
// 每页多少条数据
limit: { type: Number, default: 20 }
},
data() {
return {
loading: false,
params: this.remote.params || {},
dataList: this.data,
page: { total: 0, size: this.limit, currentPage: 1 },
moreFilterVisible: false
}
},
watch: {
'remote.params': {
immediate: true,
handler(data) {
this.params = data || {}
}
},
data: {
immediate: true,
handler(data) {
dataList.value = data
}
}
},
computed: {
table() {
return this.$refs.table
},
hasMoreFilter() {
return !!this.moreFilters.length
}
},
methods: {
fetchList(isReset) {
/**
* @param function httpRequest api接口
* @param function beforeRequest 接口请求之前
* @param function callback 接口请求成功回调
*/
const { httpRequest, beforeRequest, callback } = this.remote
if (!httpRequest) {
return
}
// 参数设置
let params = this.params
// 翻页参数设置
if (this.hasPagination) {
params.page = page.currentPage
params.page_size = page.size
}
// 接口请求之前
if (beforeRequest) {
params = beforeRequest(params, isReset)
}
// for (const key in params) {
// if (params[key] === '' || params[key] === undefined || params[key] === undefined) {
// delete params[key]
// }
// }
loading.value = true
httpRequest(params)
.then(res => {
const { list = [], total = 0 } = res.data || {}
page.total = total
dataList.value = callback ? callback(list) : list
})
.catch(() => {
page.total = 0
dataList.value = []
})
.finally(() => {
loading.value = false
})
},
// 搜索
search() {
page.currentPage = 1
this.fetchList()
},
// 重置
reset() {
// 清空筛选条件
this.$refs.filterForm && this.$refs.filterForm.resetFields()
// 清空更多筛选条件
this.hasMoreFilter && this.$refs.moreFilterForm && this.$refs.moreFilterForm.resetFields()
// 初始化页码
page.currentPage = 1
// 刷新列表
this.fetchList(true)
},
// 刷新
refetch(isForce) {
isForce ? this.reset() : this.fetchList()
},
// 页数改变
pageSizeChange(value) {
page.currentPage = 1
page.size = value
this.fetchList()
},
visible(item) {
return Object.prototype.hasOwnProperty.call(item, 'visible') ? item.visible : true
},
// 显示更多筛选
showMoreFilter() {
this.moreFilterVisible = true
},
// 取消更多筛选
cancelMoreFilter() {
this.moreFilterVisible = false
// 清空筛选条件
this.$refs.moreFilterForm && this.$refs.moreFilterForm.resetFields()
},
// 确定更多筛选
primaryMoreFilter() {
this.moreFilterVisible = false
this.search()
}
},
beforeMount() {
this.fetchList()
}
}
</script> -->
<style lang="scss">
.table-list {
height: 100%;
......
......@@ -6,7 +6,7 @@ import type { UploadProps, UploadUserFile } from 'element-plus'
import md5 from 'blueimp-md5'
import { getSignature } from '@/api/base'
const props = withDefaults(defineProps<{ modelValue: string | UploadUserFile[]; prefix?: string }>(), {
const props = withDefaults(defineProps<{ modelValue: string | []; prefix?: string }>(), {
prefix: 'upload/admin/'
})
const emit = defineEmits(['update:modelValue'])
......@@ -18,7 +18,7 @@ const fileList = ref<UploadUserFile[]>([])
watch(
() => props.modelValue,
value => {
fileList.value = Array.isArray(value) ? value.map(item => ({ ...item })) : []
fileList.value = Array.isArray(value) ? [...value] : []
}
)
......@@ -27,7 +27,7 @@ const showFileList = computed(() => {
})
// 上传之前
const handleBeforeUpload: UploadProps['beforeUpload'] = async file => {
const handleBeforeUpload = async (file: any) => {
const fileName = file.name
const key = props.prefix + md5(fileName + new Date().getTime()) + fileName.substr(fileName.lastIndexOf('.'))
const response: Record<string, any> = await getSignature()
......@@ -39,15 +39,22 @@ const handleBeforeUpload: UploadProps['beforeUpload'] = async file => {
success_action_status: '200',
url: `${response.host}/${key}`
}
file.url = `${response.host}/${key}`
}
// 上传成功
const handleSuccess: UploadProps['onSuccess'] = (response, file) => {
const value = showFileList.value
? [...props.modelValue, { name: file.name, url: uploadData.value.url }]
: uploadData.value.url
console.log(value)
emit('update:modelValue', value)
const handleSuccess = (response: any, file: any, files: any) => {
if (!files.every((item: any) => item.status === 'success')) return
if (showFileList.value) {
emit(
'update:modelValue',
files.map((item: any) => {
return { name: item.name, url: item.url || item.raw.url }
})
)
} else {
emit('update:modelValue', file.raw.url)
}
}
// 上传限制
......@@ -57,8 +64,12 @@ const handleExceed: UploadProps['onExceed'] = () => {
// 删除
const handleRemove: UploadProps['onRemove'] = (file, files) => {
console.log(file, files)
// const value = showFileList.value ? props.modelValue.filter(item => item.url !== file.url) : ''
emit(
'update:modelValue',
files.map((item: any) => {
return { name: item.name, url: item.url || item.raw.url }
})
)
}
// 预览
......@@ -78,21 +89,20 @@ const handlePreview: UploadProps['onPreview'] = uploadFile => {
:on-preview="handlePreview"
:on-success="handleSuccess"
:file-list="fileList"
class="uploader"
>
<slot>
<template v-if="showFileList">
<template v-if="$attrs['list-type'] === 'picture-card'">
<el-icon><Plus /></el-icon>
</template>
<template v-else>
<el-button type="primary">点击上传</el-button>
</template>
<template v-if="showFileList">
<template v-if="$attrs['list-type'] === 'picture-card'">
<el-icon><Plus /></el-icon>
</template>
<template v-else>
<el-button type="primary">点击上传</el-button>
</template>
<div class="avatar-uploader" v-else>
<el-image :src="(modelValue as string)" fit="contain" v-if="modelValue" />
<el-icon v-else class="avatar-uploader-icon"><Plus /></el-icon>
</div>
</slot>
</template>
<div class="avatar-uploader" v-else>
<el-image :src="(modelValue as string)" fit="contain" v-if="modelValue" />
<el-icon v-else class="avatar-uploader-icon"><Plus /></el-icon>
</div>
<template #tip>
<div class="el-upload__tip"><slot name="tip"></slot></div>
</template>
......@@ -100,6 +110,9 @@ const handlePreview: UploadProps['onPreview'] = uploadFile => {
</template>
<style lang="scss">
.uploader {
flex: 1;
}
.avatar-uploader {
width: 178px;
height: 178px;
......
<script setup lang="ts">
import Editor from '@tinymce/tinymce-vue'
import md5 from 'blueimp-md5'
import { getSignature, uploadFile } from '@/api/base'
const ImageUploadHandler = blobInfo =>
new Promise((resolve, reject) => {
const file = blobInfo.blob()
getSignature()
.then(response => {
const prefix = 'upload/admin/'
const key = prefix + md5(file.name + new Date().getTime()) + file.name.substr(file.name.lastIndexOf('.'))
const { accessid, policy, signature, host } = response
const params = {
key,
OSSAccessKeyId: accessid,
policy,
signature,
success_action_status: '200',
file,
url: `${host}/${key}`
}
uploadFile(params)
.then((res: any) => {
resolve(res.url)
})
.catch(() => {
reject('上传失败')
})
})
.catch(() => {
reject('获取Signature失败')
})
})
const init = {
language: 'zh-Hans',
height: 600,
menubar: false,
statusbar: false,
plugins: 'table charmap fullscreen lists link code preview quickbars',
toolbar:
'undo redo | fontsizeselect lineheight bold italic underline strikethrough forecolor backcolor | link quickimage image media table | align hangingindent indent outdent numlist bullist | charmap blockquote hr fullscreen | code preview',
// font_formats:
// '微软雅黑=Microsoft YaHei,Helvetica Neue,PingFang SC,sans-serif;苹果苹方=PingFang SC,Microsoft YaHei,sans-serif;宋体=simsun,serif;仿宋体=FangSong,serif;黑体=SimHei,sans-serif;Arial=arial,helvetica,sans-serif;Times New Roman',
fontsize_formats: '8px 10px 12px 14px 15px 16px 17px 18px 20px 24px',
lineheight_formats: '0.5 1 1.2 1.5 2',
images_upload_handler: ImageUploadHandler,
automatic_uploads: true,
quickbars_insert_toolbar: false,
// style_formats: [{ title: '悬挂缩进', block: 'p', styles: { textIndent: '-2em', paddingLeft: '2em' } }],
content_style: 'img {max-width:100%;}'
}
</script>
<template>
<editor :init="init" v-bind="$attrs" style="width: 100%" />
</template>
......@@ -36,7 +36,7 @@ const listOptions = computed(() => {
params: { tab: 'course', status: '' }
},
filters: [
{ label: '类型', prop: 'tab', slots: 'filter-type' },
{ prop: 'tab', slots: 'filter-type' },
{
type: 'select',
prop: 'status',
......
......@@ -10,7 +10,7 @@ const listOptions = {
httpRequest: getBannerList,
params: { type: '' }
},
filters: [{ label: '类型', prop: 'type', slots: 'filter-type' }],
filters: [{ prop: 'type', slots: 'filter-type' }],
columns: [
{ label: '封面图片', slots: 'table-cover', width: 224 },
{ label: 'ID', prop: 'id' },
......@@ -25,8 +25,8 @@ const listOptions = {
const typeList = [
{ label: '全部', value: '' },
{ label: '新闻', value: '1' },
{ label: '跳转窗口', value: '2' }
{ label: '站内文章', value: '1' },
{ label: '输入链接', value: '2' }
]
const onChangeType = () => {
......@@ -59,7 +59,14 @@ const onRemove = (row: any) => {
</el-radio-group>
</template>
<template #table-cover="{ row }">
<el-image :src="row.cover_page" lazy fit="cover" style="width: 200px; height: 100px" />
<el-image
:src="row.cover_page + '?x-oss-process=image/resize,m_fill,h_100,w_200'"
:preview-src-list="[row.cover_page]"
preview-teleported
lazy
fit="cover"
style="width: 200px; height: 100px"
/>
</template>
<template #table-operate="{ row }">
<el-space>
......
......@@ -3,6 +3,7 @@ import { ref, reactive, onMounted } from 'vue'
import { useRouter } from 'vue-router'
import { ElMessage } from 'element-plus'
import type { FormInstance } from 'element-plus'
import AppEditor from '@/components/tinymce/Index.vue'
import { createBanner, updateBanner, getBanner } from '../api'
const props = defineProps<{ id?: string }>()
......@@ -13,15 +14,10 @@ const formRef = ref<FormInstance>()
const form = reactive({ title: '', type: '2', desc: '', weight: '', cover_page: '', url: '' })
const rules = {
title: [{ required: true, message: '请输入标题', trigger: 'blur' }],
type: [{ required: true, message: '请选择类型', trigger: 'change' }],
cover_page: [{ required: true, message: '请上传封面图', trigger: 'change' }],
url: [{ required: true, message: '请输入跳转链接', trigger: 'blur' }],
desc: [{ required: true, message: '请输入资料详情', trigger: 'blur' }]
url: [{ required: true, message: '请输入文章链接', trigger: 'blur' }],
desc: [{ required: true, message: '请输入正文', trigger: 'blur' }]
}
const typeList = [
{ label: '新闻', value: '1' },
{ label: '跳转窗口', value: '2' }
]
// 提交
const onSubmit = (formRef: FormInstance) => {
if (!formRef) return
......@@ -29,6 +25,10 @@ const onSubmit = (formRef: FormInstance) => {
props.id ? update() : create()
})
}
// 取消
const onCancel = () => {
router.replace('/banner')
}
// 创建
const create = () => {
createBanner(form).then(() => {
......@@ -59,25 +59,27 @@ onMounted(() => {
<el-form-item label="标题" prop="title">
<el-input v-model="form.title" />
</el-form-item>
<el-form-item label="类型" prop="type">
<el-select v-model="form.type">
<el-option v-for="item in typeList" :label="item.label" :value="item.value" :key="item.value"></el-option>
</el-select>
</el-form-item>
<el-form-item label="封面" prop="cover_page">
<AppUpload v-model="form.cover_page" accept="image/*"></AppUpload>
</el-form-item>
<el-form-item label="跳转链接" prop="url" v-if="form.type === '2'">
<el-form-item label="链接内容" prop="type">
<el-radio-group v-model="form.type">
<el-radio label="1">站内文章</el-radio>
<el-radio label="2">输入链接</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="文章链接" prop="url" v-if="form.type === '2'">
<el-input v-model="form.url" />
</el-form-item>
<el-form-item label="详情" prop="desc" v-if="form.type === '1'">
<el-input type="textarea" v-model="form.desc" :autosize="{ minRows: 12 }" />
<el-form-item label="正文" prop="desc" v-if="form.type === '1'">
<AppEditor v-model="form.desc"></AppEditor>
</el-form-item>
<el-form-item label="权重" prop="weight">
<el-input type="number" v-model="form.weight" />
</el-form-item>
<el-form-item>
<el-button type="primary" auto-insert-space @click="onSubmit(formRef)">保存</el-button>
<el-button auto-insert-space @click="onCancel">取消</el-button>
</el-form-item>
</el-form>
</AppCard>
......
import httpRequest from '@/utils/axios'
// 获取学员列表
// 获取课程列表
export function getCourseList(params?: { name?: string; page?: number; page_size?: number }) {
return httpRequest.get('/api/psp/backend/course/index', { params })
}
// 更新课程信息
export function updateCourse(data: { id: string; weight?: string; status?: string }) {
return httpRequest.post('/api/psp/backend/course/update', data)
}
<template>
<el-dialog :title="title" :close-on-click-modal="false" v-bind="$attrs" width="400px">
<el-form ref="form" :model="form" :rules="rules">
<el-form-item label="权重" prop="sort">
<el-input-number v-model="form.sort" placeholder="请输入内容" size="small" :step-strictly="true" />
</el-form-item>
</el-form>
<template #footer>
<el-button type="text" @click="onCancel">取消</el-button>&nbsp;&nbsp;
<el-button type="primary" size="medium" @click="onPrimary">保存</el-button>
</template>
</el-dialog>
</template>
<script>
// lodash
import { updateCourse } from '../api.js'
import { pick } from 'lodash'
export default {
props: {
isEdit: { type: Boolean, default: false },
data: { type: Object, default: () => ({}) }
},
data() {
return {
form: { sort: '0' },
rules: {
sort: { required: true, message: '请输入权重', trigger: 'change' }
}
}
},
watch: {
data: {
immediate: true,
handler(data) {
this.form = Object.assign({}, this.form, data)
}
}
},
computed: {
title() {
return this.isEdit ? '更新课程' : '创建课程'
}
},
methods: {
// 取消
onCancel() {
this.$emit('update:visible', false)
},
// 确定
onPrimary() {
this.$refs.form.validate().then(() => {
const params = pick(this.form, ['id', 'sort'])
this.isEdit ? this.edit(params) : this.create(params)
})
},
// 创建课程
create(params) {
updateCourse(params).then(res => {
this.$message.success('创建成功')
this.$emit('update:visible', false)
this.$emit('success', res.data)
})
},
// 编辑权限
edit(params) {
updateCourse(params).then(res => {
this.$message.success('修改成功')
this.$emit('update:visible', false)
this.$emit('success', res.data)
})
}
}
}
</script>
<style></style>
<script setup lang="ts">
import { getCourseList } from '../api'
import { ref } from 'vue'
import { ElMessage } from 'element-plus'
import { getCourseList, updateCourse } from '../api'
const appList = ref()
const listOptions = {
remote: {
......@@ -9,25 +14,51 @@ const listOptions = {
filters: [{ type: 'input', prop: 'name', placeholder: '课程名称' }],
columns: [
{ label: '课程图片', slots: 'table-picture', width: 224 },
{ label: 'ID', prop: 'id' },
{ label: 'ID', prop: 'id', width: 200 },
{ label: '课程名称', prop: 'course_name' },
{ label: '课程描述', prop: 'course_represent', slots: 'table-desc' },
{ label: '权重', prop: 'weight' },
{ label: '是否显示', prop: 'status' },
{ label: '权重', prop: 'weight', width: 80, align: 'center' },
{ label: '是否显示', prop: 'status', slots: 'table-status', width: 160, align: 'center' },
{ label: '操作', slots: 'table-operate', width: 100, align: 'right' }
]
}
const onChangeStatus = (row: Record<string, any>) => {
if (!row.id) return
const params = { id: row.id, status: row.status === '1' ? '2' : '1' }
updateCourse(params).then(() => {
ElMessage({ type: 'success', message: '更新成功' })
appList.value?.refetch()
})
}
</script>
<template>
<AppCard>
<AppList v-bind="listOptions">
<AppList v-bind="listOptions" ref="appList">
<template #table-picture="{ row }">
<el-image :src="row.course_picture" lazy fit="cover" style="width: 200px; height: 100px" />
<el-image
:src="row.course_picture + '?x-oss-process=image/resize,m_fill,h_100,w_200'"
:preview-src-list="[row.course_picture]"
preview-teleported
lazy
fit="cover"
style="width: 200px; height: 100px"
/>
</template>
<template #table-desc="{ row }">
<div v-html="row.course_represent" style="max-height: 100px"></div>
</template>
<template #table-status="{ row }">
<el-switch
:value="row.status"
active-text="显示"
active-value="2"
inactive-text="不显示"
inactive-value="1"
@change="onChangeStatus(row)"
/>
</template>
<template #table-operate>
<el-button>更新</el-button>
</template>
......
......@@ -10,7 +10,7 @@ const listOptions = {
httpRequest: getDocList,
params: { type: '' }
},
filters: [{ label: '类型', prop: 'type', slots: 'filter-type' }],
filters: [{ prop: 'type', slots: 'filter-type' }],
columns: [
{ label: 'ID', prop: 'id' },
{ label: '标题', prop: 'title' },
......
......@@ -3,6 +3,7 @@ import { ref, reactive, onMounted } from 'vue'
import { useRouter } from 'vue-router'
import { ElMessage } from 'element-plus'
import type { FormInstance } from 'element-plus'
import AppEditor from '@/components/tinymce/Index.vue'
import { createDoc, updateDoc, getDoc } from '../api'
const props = defineProps<{ id?: string }>()
......@@ -10,11 +11,12 @@ const props = defineProps<{ id?: string }>()
const router = useRouter()
const formRef = ref<FormInstance>()
const form = reactive({ title: '', type: '', desc: '', weight: '', file: [] })
const form = reactive({ title: '', type: '', desc: '', weight: '', file: [], desc_type: '1', url: '' })
const rules = {
title: [{ required: true, message: '请输入标题', trigger: 'blur' }],
type: [{ required: true, message: '请选择类型', trigger: 'change' }],
desc: [{ required: true, message: '请输入资料详情', trigger: 'blur' }]
url: [{ required: true, message: '请输入文章链接', trigger: 'blur' }],
desc: [{ required: true, message: '请输入正文', trigger: 'blur' }]
}
const typeList = [
{ label: '入学指南', value: '1' },
......@@ -71,10 +73,19 @@ onMounted(() => {
<el-option v-for="item in typeList" :label="item.label" :value="item.value" :key="item.value"></el-option>
</el-select>
</el-form-item>
<el-form-item label="详情" prop="desc">
<el-input type="textarea" v-model="form.desc" :autosize="{ minRows: 12 }" />
<el-form-item label="链接内容" prop="desc_type">
<el-radio-group v-model="form.desc_type">
<el-radio label="1">站内文章</el-radio>
<el-radio label="2">输入链接</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="附件" prop="file">
<el-form-item label="文章链接" prop="url" v-if="form.desc_type === '2'">
<el-input v-model="form.url" />
</el-form-item>
<el-form-item label="正文" prop="desc" v-if="form.desc_type === '1'">
<AppEditor v-model="form.desc"></AppEditor>
</el-form-item>
<el-form-item label="附件" prop="file" v-if="form.desc_type === '1'">
<app-upload v-model="form.file"></app-upload>
</el-form-item>
<el-form-item label="权重" prop="weight">
......
......@@ -21,8 +21,8 @@ const form = reactive({
})
const rules = {
name: [{ required: true, message: '请输入姓名', trigger: 'blur' }],
mobile: [{ required: true, message: '请输入手机号', trigger: 'blur' }],
certificate_number: [{ required: true, message: '请输入证书编号', trigger: 'blur' }]
mobile: [{ required: true, message: '请输入手机号', trigger: 'blur' }]
// certificate_number: [{ required: true, message: '请输入证书编号', trigger: 'blur' }]
}
// 提交
const onSubmit = (formRef: FormInstance) => {
......
......@@ -7,8 +7,9 @@ const listOptions = {
params: { name: '', status: '' }
},
filters: [
{ type: 'input', prop: 'name', placeholder: '团队名称' },
{ label: '名称', type: 'input', prop: 'name', placeholder: '团队名称' },
{
label: '状态',
type: 'select',
prop: 'status',
placeholder: '状态',
......@@ -20,7 +21,7 @@ const listOptions = {
}
],
columns: [
{ label: 'Logo', prop: 'logo', slots: 'table-logo' },
{ label: 'Logo', prop: 'logo', slots: 'table-logo', width: 224 },
{ label: '团队名称', prop: 'name' },
{ label: '团队口号', prop: 'slogan' },
{ label: '团队简介', prop: 'brief' },
......@@ -28,7 +29,7 @@ const listOptions = {
{ label: '积分', prop: 'star' },
{ label: '资料数量', prop: 'files_count' },
{ label: '问答数量', prop: 'questions_count' },
{ label: '审核', prop: 'status', slots: 'table-status' },
{ label: '审核', prop: 'status', slots: 'table-status', width: 160, align: 'center' },
{ label: '操作', slots: 'table-operate', width: 100, align: 'right' }
]
}
......@@ -38,7 +39,14 @@ const listOptions = {
<AppCard>
<AppList v-bind="listOptions">
<template #table-logo="{ row }">
<el-image :src="row.logo" lazy />
<el-image
:src="row.logo + '?x-oss-process=image/resize,m_fill,h_100,w_200'"
:preview-src-list="[row.logo]"
preview-teleported
lazy
fit="cover"
style="width: 200px; height: 100px"
/>
</template>
<template #table-status="{ row }">
<el-switch :value="row.status" active-text="通过" active-value="1" inactive-text="未通过" inactive-value="2" />
......
......@@ -10,7 +10,7 @@ const listOptions = {
httpRequest: getVideoList,
params: { type: '' }
},
filters: [{ label: '类型', prop: 'type', slots: 'filter-type' }],
filters: [{ prop: 'type', slots: 'filter-type' }],
columns: [
{ label: '封面', slots: 'table-cover', width: 224 },
{ label: 'ID', prop: 'id' },
......@@ -59,16 +59,23 @@ const onRemove = (row: any) => {
</template>
<template #table-cover="{ row }">
<el-image :src="row.cover_page" lazy fit="cover" style="width: 200px; height: 100px" />
<el-image
:src="row.cover_page + '?x-oss-process=image/resize,m_fill,h_100,w_200'"
:preview-src-list="[row.cover_page]"
preview-teleported
lazy
fit="cover"
style="width: 200px; height: 100px"
/>
</template>
<template #table-operate="{ row }">
<el-space>
<router-link :to="`/video/update/${row.id}`">
<el-button plain>编辑</el-button>
</router-link>
<router-link :to="`/video/view/${row.id}`">
<!-- <router-link :to="`/video/view/${row.id}`">
<el-button type="primary" plain>查看</el-button>
</router-link>
</router-link> -->
<el-button type="danger" plain @click="onRemove(row)">删除</el-button>
</el-space>
</template>
......
......@@ -8,9 +8,11 @@ const router = createRouter({
router.beforeEach(async (to, from, next) => {
const user = useUserStore()
await user.getUser()
if (!user.isLogin) {
location.href = `${import.meta.env.VITE_LOGIN_URL}?rd=${encodeURIComponent(location.href)}`
await user.getUser()
user.isLogin
? next()
: (location.href = `${import.meta.env.VITE_LOGIN_URL}?rd=${encodeURIComponent(location.href)}`)
}
next()
})
......
......@@ -18,12 +18,7 @@ export default defineConfig(({ mode }) => {
cert: fs.readFileSync(path.join(__dirname, './https/dev.ezijing.com.pem'))
},
proxy: {
'/api/psp': {
target: 'https://psp-api.ezijing.com',
changeOrigin: true,
rewrite: path => path.replace('/api/psp/', '/')
},
'/api': 'https://project-api.ezijing.com'
'/api': 'https://psp-center.ezijing.com'
}
},
resolve: {
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论