@@ -16,6 +16,7 @@ | |||
"dayjs": "^1.11.2", | |||
"mockjs": "^1.1.0", | |||
"pinia": "^2.0.13", | |||
"pinia-plugin-persist": "^1.0.0", | |||
"tinymce": "^5.10.2", | |||
"vue": "^3.2.16", | |||
"vue-router": "^4.0.14", | |||
@@ -8380,6 +8381,49 @@ | |||
} | |||
} | |||
}, | |||
"node_modules/pinia-plugin-persist": { | |||
"version": "1.0.0", | |||
"resolved": "https://registry.npmjs.org/pinia-plugin-persist/-/pinia-plugin-persist-1.0.0.tgz", | |||
"integrity": "sha512-M4hBBd8fz/GgNmUPaaUsC29y1M09lqbXrMAHcusVoU8xlQi1TqgkWnnhvMikZwr7Le/hVyMx8KUcumGGrR6GVw==", | |||
"dependencies": { | |||
"vue-demi": "^0.12.1" | |||
}, | |||
"peerDependencies": { | |||
"@vue/composition-api": "^1.0.0", | |||
"pinia": "^2.0.0", | |||
"vue": "^2.0.0 || >=3.0.0" | |||
}, | |||
"peerDependenciesMeta": { | |||
"@vue/composition-api": { | |||
"optional": true | |||
} | |||
} | |||
}, | |||
"node_modules/pinia-plugin-persist/node_modules/vue-demi": { | |||
"version": "0.12.5", | |||
"resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.12.5.tgz", | |||
"integrity": "sha512-BREuTgTYlUr0zw0EZn3hnhC3I6gPWv+Kwh4MCih6QcAeaTlaIX0DwOVN0wHej7hSvDPecz4jygy/idsgKfW58Q==", | |||
"hasInstallScript": true, | |||
"bin": { | |||
"vue-demi-fix": "bin/vue-demi-fix.js", | |||
"vue-demi-switch": "bin/vue-demi-switch.js" | |||
}, | |||
"engines": { | |||
"node": ">=12" | |||
}, | |||
"funding": { | |||
"url": "https://github.com/sponsors/antfu" | |||
}, | |||
"peerDependencies": { | |||
"@vue/composition-api": "^1.0.0-rc.1", | |||
"vue": "^3.0.0-0 || ^2.6.0" | |||
}, | |||
"peerDependenciesMeta": { | |||
"@vue/composition-api": { | |||
"optional": true | |||
} | |||
} | |||
}, | |||
"node_modules/pinia/node_modules/vue-demi": { | |||
"version": "0.13.1", | |||
"resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.13.1.tgz", | |||
@@ -18456,6 +18500,22 @@ | |||
} | |||
} | |||
}, | |||
"pinia-plugin-persist": { | |||
"version": "1.0.0", | |||
"resolved": "https://registry.npmjs.org/pinia-plugin-persist/-/pinia-plugin-persist-1.0.0.tgz", | |||
"integrity": "sha512-M4hBBd8fz/GgNmUPaaUsC29y1M09lqbXrMAHcusVoU8xlQi1TqgkWnnhvMikZwr7Le/hVyMx8KUcumGGrR6GVw==", | |||
"requires": { | |||
"vue-demi": "^0.12.1" | |||
}, | |||
"dependencies": { | |||
"vue-demi": { | |||
"version": "0.12.5", | |||
"resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.12.5.tgz", | |||
"integrity": "sha512-BREuTgTYlUr0zw0EZn3hnhC3I6gPWv+Kwh4MCih6QcAeaTlaIX0DwOVN0wHej7hSvDPecz4jygy/idsgKfW58Q==", | |||
"requires": {} | |||
} | |||
} | |||
}, | |||
"platform": { | |||
"version": "1.3.6", | |||
"resolved": "https://registry.npmjs.org/platform/-/platform-1.3.6.tgz", |
@@ -0,0 +1,47 @@ | |||
import { defAxios as request } from '@/utils/http' | |||
/** | |||
* 获取云盒列表(分页) | |||
* @param {Object} | |||
* @returns | |||
*/ | |||
export function bannerPage(params) { | |||
return request({ | |||
url: '/pilotAd/index', | |||
method: 'get', | |||
params | |||
}) | |||
} | |||
/** | |||
* 创建盒子 | |||
* @returns | |||
*/ | |||
export function bannerCreate(data) { | |||
return request({ | |||
url: '/pilotAd/add', | |||
method: 'post', | |||
data | |||
}) | |||
} | |||
/** | |||
* 编辑盒子 | |||
* @returns | |||
*/ | |||
export function bannerUpdate(data) { | |||
return request({ | |||
url: '/pilotAd/edit', | |||
method: 'put', | |||
data | |||
}) | |||
} | |||
/** | |||
* 删除盒子 | |||
* @returns | |||
*/ | |||
export function bannerDelete(ids) { | |||
return request({ | |||
url: `/pilotAd/delete/${ids}`, | |||
method: 'delete' | |||
}) | |||
} |
@@ -5,6 +5,9 @@ import { createApp } from 'vue' | |||
import { setupRouter } from '@/router' | |||
import { setupStore } from '@/store' | |||
import { useMessage } from 'naive-ui' | |||
window.$message = useMessage() | |||
import App from './App.vue' | |||
function setupApp() { |
@@ -1,7 +1,10 @@ | |||
const common = { | |||
primaryColor: '#36ad6a', | |||
primaryColor: 'rgba(24, 144, 255, 1)', | |||
textColor: 'rgba(51, 51, 51, 1)', | |||
whiteColor: 'rgba(255, 255, 255, 1)' | |||
whiteColor: 'rgba(255, 255, 255, 1)', | |||
errorColor: '#ff3333', | |||
primaryColorHover: 'rgba(24, 144, 255, 0.8)', | |||
primaryColorPressed: 'rgba(24, 144, 255, 1)' | |||
} | |||
const themeOverrides = { |
@@ -0,0 +1,158 @@ | |||
<template> | |||
<Modal | |||
:options="getModalOptions" | |||
:on-positive-click="handleConfirm" | |||
:on-negative-click="handleClose" | |||
:on-close="handleClose" | |||
> | |||
<template #Context> | |||
<n-form | |||
ref="formRef" | |||
:model="form" | |||
:rules="rules" | |||
:label-width="80" | |||
label-placement="left" | |||
require-mark-placement="left" | |||
> | |||
<n-form-item label="横幅图片" path="cover"> | |||
<UploadOss ref="ossRefs" :default-list="initUpload" @upload-status="handleUploadStatus" /> | |||
</n-form-item> | |||
<!-- <n-form-item label="排序" path="sort"> | |||
<n-input-number v-model:value="form.sort" clearable /> | |||
</n-form-item> --> | |||
</n-form> | |||
</template> | |||
</Modal> | |||
</template> | |||
<script> | |||
import { defineComponent, ref, reactive, computed, toRefs, watch } from 'vue' | |||
import Modal from '@/components/Modal/index.vue' | |||
import UploadOss from '@/components/UploadOss/index.vue' | |||
import { bannerCreate, bannerUpdate } from '@/api/system/banner.js' | |||
export default defineComponent({ | |||
name: 'BannerModal', | |||
components: { Modal, UploadOss }, | |||
props: { | |||
visible: { | |||
type: Boolean, | |||
default: false | |||
}, | |||
type: { | |||
type: String, | |||
default: 'create' | |||
}, | |||
data: { | |||
type: Object, | |||
default: () => null | |||
} | |||
}, | |||
emits: { | |||
'update:visible': null, | |||
'reload': null | |||
}, | |||
setup(props, { emit }) { | |||
const MODAL_TYPE = { | |||
'create': '新建横幅', | |||
'preview': '横幅详情', | |||
'update': '编辑横幅' | |||
} | |||
const data = reactive({ | |||
form: { ...props.data }, | |||
initUpload: '', | |||
rules: { | |||
cover: [{ required: true, message: '请选择横幅图片', type: 'string', trigger: 'blur' }] | |||
} | |||
}) | |||
watch(() => props.visible, (value) => { | |||
if (value) { | |||
if (props.data) { | |||
data.initUpload = props.data.cover | |||
} | |||
} else { | |||
data.initUpload = '' | |||
} | |||
}) | |||
const getModalOptions = computed(() => { | |||
return { | |||
show: props.visible, | |||
title: MODAL_TYPE[props.type], | |||
negativeText: '取消', | |||
positiveText: '确认' | |||
} | |||
}) | |||
function handleUploadStatus(status) { | |||
data.form.cover = status | |||
} | |||
const formRef = ref() | |||
const ossRefs = ref() | |||
function handleConfirm() { | |||
if (props.type !== 'preview') { | |||
formRef.value?.validate((errors) => { | |||
if (!errors) { | |||
const uploads = ossRefs.value.map((item, index) => { | |||
return item.startUpload() | |||
}) | |||
Promise.all(uploads) | |||
.then(response => { | |||
const isError = response.map((item) => { | |||
return item.includes('error') | |||
}) | |||
if (!isError.includes(true)) { | |||
const imageStr = response.join() | |||
const params = { | |||
...data.form, | |||
cover: imageStr[0] | |||
} | |||
if (params.id) { | |||
/* 编辑 */ | |||
bannerUpdate(params) | |||
.then(res => { | |||
if (res.code === 0) { | |||
emit('reload') | |||
handleClose() | |||
} | |||
}) | |||
} else { | |||
/* 新增 */ | |||
bannerCreate(params) | |||
.then(res => { | |||
if (res.code === 0) { | |||
emit('reload') | |||
handleClose() | |||
} | |||
}) | |||
} | |||
} else { | |||
$message.error('图片上传失败,请稍后重试') | |||
} | |||
}) | |||
handleClose() | |||
} else { | |||
$message.error('请完善必填信息') | |||
} | |||
}) | |||
} else { | |||
handleClose() | |||
} | |||
} | |||
/* 关闭弹窗 */ | |||
const handleClose = () => { | |||
emit('update:visible', false) | |||
} | |||
return { | |||
...toRefs(data), | |||
getModalOptions, | |||
handleUploadStatus, | |||
handleConfirm, | |||
handleClose | |||
} | |||
} | |||
}) | |||
</script> |
@@ -1,14 +1,60 @@ | |||
<template> | |||
<div> | |||
移动端横幅管理 | |||
<n-card> | |||
<!-- <headSearch :info="search" @search="handleSearch" @reset="handleSearch" /> --> | |||
<data-table | |||
ref="tableRef" | |||
:columns="columns" | |||
:row-key="(row) => row.id" | |||
:request="loadDataTable" | |||
size="large" | |||
@update:checked-row-keys="handleCheck" | |||
> | |||
<template #tableTitle> | |||
<n-button type="primary" @click="handleModal"> 新建 </n-button> | |||
</template> | |||
</data-table> | |||
</n-card> | |||
</div> | |||
<!-- 新增、编辑弹窗 --> | |||
<BannerModal v-if="modalShow" v-model:visible="modalShow" :type="modalType" :data="rowData" @reload="handleSearch" /> | |||
</template> | |||
<script> | |||
import { unref, toRefs, reactive } from 'vue' | |||
// import headSearch from '@/components/Search/index.vue' | |||
import table from './tool/table.js' | |||
import dataTable from '@/components/DataTable/index.vue' | |||
import BannerModal from './BannerModal.vue' | |||
import { bannerPage } from '@/api/system/banner.js' | |||
export default { | |||
name: 'BannerManage', | |||
components: { dataTable, BannerModal }, | |||
setup() { | |||
const data = reactive({ | |||
...toRefs(table) | |||
}) | |||
const loadDataTable = async(res) => { | |||
const _params = { | |||
...unref(data.searchParams), | |||
...res | |||
} | |||
return await bannerPage(_params) | |||
} | |||
// 新增 | |||
function handleModal() { | |||
data.rowData = null | |||
data.modalType = 'create' | |||
data.modalShow = true | |||
} | |||
return { | |||
...toRefs(data), | |||
loadDataTable, | |||
handleModal | |||
} | |||
} | |||
} | |||
@@ -0,0 +1,19 @@ | |||
import { reactive } from 'vue' | |||
export const search = reactive({ | |||
search: [ | |||
{ | |||
label: '用户账号', | |||
key: 'username', | |||
props: { | |||
placeholder: '请输入用户账号' | |||
} | |||
}, | |||
{ | |||
label: '用户姓名', | |||
key: 'realname', | |||
props: { | |||
placeholder: '请输入用户姓名' | |||
} | |||
} | |||
] | |||
}) |
@@ -0,0 +1,133 @@ | |||
import { h, ref, reactive } from 'vue' | |||
import TableImage from '@/components/DataTable/tools/Image.vue' | |||
import TableAction from '@/components/DataTable/tools/Action.vue' | |||
import { bannerDelete } from '@/api/system/banner.js' | |||
/* 注册table */ | |||
const tableRef = ref() | |||
const searchParams = ref() | |||
function handleSearch(params) { | |||
searchParams.value = { ...params } | |||
tableRef.value.reFetch({ searchParams }) | |||
} | |||
/** | |||
* @description: 获取数据及操作 | |||
* @param {*} row 单行数据 | |||
* @param {*} type 操作类型 create:创建,preview:预览,edit:编辑 | |||
* @return {*} | |||
*/ | |||
function getRowData(row, type) { | |||
data.rowData = row | |||
data.modalType = type | |||
data.modalShow = true | |||
} | |||
/** | |||
* @description: 删除用户接口 | |||
* @param {Array} ids 用户id集合 | |||
* @return {*} | |||
*/ | |||
function deleteData(ids) { | |||
bannerDelete(ids).then((res) => { | |||
if (res.code === 0) { | |||
handleSearch() | |||
} | |||
}).catch(e => { | |||
console.log(e) | |||
}) | |||
} | |||
const data = reactive({ | |||
tableRef, | |||
searchParams, | |||
rowData: {}, | |||
modalType: 'create', | |||
modalShow: false, | |||
handleSearch, | |||
deleteData, | |||
columns: [ | |||
{ | |||
title: '序号', | |||
key: 'key', | |||
render: (_, index) => { | |||
return `${index + 1}` | |||
}, | |||
align: 'center' | |||
}, | |||
{ | |||
title: '横幅图片', | |||
key: 'cover', | |||
align: 'center', | |||
render(row) { | |||
return h(TableImage, { | |||
images: { | |||
width: 36, | |||
height: 36, | |||
src: row.cover | |||
} | |||
}) | |||
} | |||
}, | |||
{ | |||
title: '上传人', | |||
key: 'updateUser', | |||
align: 'center', | |||
width: 160 | |||
}, | |||
{ | |||
title: '上传时间', | |||
key: 'updateTime', | |||
align: 'center', | |||
width: 160 | |||
}, | |||
{ | |||
title: '操作', | |||
align: 'center', | |||
width: 150, | |||
fixed: 'right', | |||
render(row) { | |||
return h(TableAction, { | |||
actions: [ | |||
{ | |||
label: '详情', | |||
type: 'button', | |||
props: { | |||
type: 'primary', | |||
text: true, | |||
onClick: getRowData.bind(null, row, 'preview') | |||
}, | |||
auth: 'basic_list' | |||
}, | |||
{ | |||
label: '修改', | |||
type: 'button', | |||
props: { | |||
type: 'primary', | |||
text: true, | |||
onClick: getRowData.bind(null, row, 'update') | |||
}, | |||
auth: 'basic_list' | |||
}, | |||
{ | |||
label: '删除', | |||
type: 'popconfirm', | |||
auth: 'basic_list', | |||
tip: '确定删除这条数据吗?', | |||
props: { | |||
onPositiveClick: deleteData.bind(null, [row.id]) | |||
}, | |||
ButtonProps: { | |||
text: true, | |||
type: 'error' | |||
} | |||
} | |||
], | |||
align: 'center' | |||
}) | |||
} | |||
} | |||
] | |||
}) | |||
export default data |
@@ -1,17 +1,176 @@ | |||
<template> | |||
<div> | |||
任务管理 | |||
<div class="task-wrapper"> | |||
<div class="task-search"> | |||
<n-input v-model:value="stateVal" autosize class="task-inp" type="text" placeholder="任务状态" /> | |||
<n-input v-model:value="pingVal" autosize class="task-inp" type="text" placeholder="任务状态" /> | |||
<n-input v-model:value="zuVal" autosize class="task-inp" type="text" placeholder="任务状态" /> | |||
<n-date-picker v-model:value="dateVal" class="task-inp" type="date" /> | |||
<n-input v-model:value="stateVal2" autosize class="task-inp" type="text" placeholder="任务状态" /> | |||
<div class="btns"> | |||
<n-button @click="resetHandle" class="reset">重置</n-button> | |||
<n-button @click="searchHandle" type="info">搜索</n-button> | |||
</div> | |||
</div> | |||
<div class="task-btns"> | |||
<n-button type="info" color="rgba(24, 144, 255, 1)" width="50" @click="addTask"> | |||
<n-icon size="20" :component="AddOutline" /> 新建 | |||
</n-button> | |||
</div> | |||
<div class="task-tables"> | |||
<n-data-table :columns="columns" :data="data" :pagination="pagination" :bordered="false" /> | |||
</div> | |||
</div> | |||
</template> | |||
<script> | |||
export default { | |||
name: 'TaskManage', | |||
setup() { | |||
<script setup name="TaskManage"> | |||
import { h, ref, reactive } from "vue"; | |||
import { NButton, NIcon, useMessage, useDialog } from "naive-ui"; | |||
import { AddOutline } from "@vicons/ionicons5"; | |||
} | |||
// const message = useMessage(); | |||
// console.log(useMessage()); | |||
const dialog = useDialog(); | |||
const stateVal = ref(''); // 任务状态 | |||
const pingVal = ref(''); // 平台名称 | |||
const zuVal = ref(''); // 租户名称 | |||
const dateVal = ref(null); // 日期 | |||
const stateVal2 = ref(''); // 任务状态? | |||
// 表格 - 内容 | |||
const data = reactive([ | |||
{ no: 3, title: "Wonderwall", length: "4:18" }, | |||
{ no: 4, title: "Don't Look Back in Anger", length: "4:48" }, | |||
{ no: 12, title: "Champagne Supernova", length: "7:27" } | |||
]); | |||
// 表格 - 列 | |||
const columns = reactive( | |||
[ | |||
{ | |||
title: "No", | |||
key: "no" | |||
}, | |||
{ | |||
title: "Title", | |||
key: "title" | |||
}, | |||
{ | |||
title: "Length", | |||
key: "length" | |||
}, | |||
{ | |||
title: "Action", | |||
key: "actions", | |||
render(row) { | |||
return h( | |||
'div', null, [ | |||
h(NButton, { | |||
quaternary: true, | |||
type: 'info', | |||
size: "small", | |||
onClick: () => console.log('详情', row) | |||
}, { default: () => '详情' }), | |||
h(NButton, { | |||
quaternary: true, | |||
type: 'info', | |||
size: "small", | |||
style: { | |||
// display: 'none', | |||
}, | |||
onClick: () => console.log('编辑', row) | |||
}, { default: () => '编辑' }), | |||
h(NButton, { | |||
quaternary: true, | |||
type: 'error', | |||
size: "small", | |||
style: { | |||
display: row.no == 12 ? 'none' : '', | |||
}, | |||
onClick: () => delHandle(row) | |||
}, { default: () => '删除' }), | |||
] | |||
); | |||
} | |||
} | |||
] | |||
); | |||
// 分页配置 | |||
const pagination = reactive({ | |||
pageSize: 10 | |||
}); | |||
// 重置 | |||
const resetHandle = () => { | |||
stateVal.value = ''; | |||
pingVal.value = ''; | |||
zuVal.value = ''; | |||
dateVal.value = null; | |||
stateVal2.value = ''; | |||
} | |||
// 搜索 | |||
const searchHandle = () => { | |||
} | |||
// 删除 | |||
const delHandle = (row) => { | |||
dialog.warning({ | |||
title: "警告", | |||
content: "你确定要删除此条内容?", | |||
positiveText: "删除", | |||
negativeText: "取消", | |||
onPositiveClick: () => { | |||
window.$message.success('删除成功!!') | |||
}, | |||
onNegativeClick: () => { | |||
console.log("取消", row); | |||
} | |||
}); | |||
} | |||
// 新增 | |||
const addTask = () => { | |||
console.log('新增'); | |||
} | |||
</script> | |||
<style scoped lang='scss'> | |||
</style> | |||
<style lang="scss" scoped> | |||
.task-wrapper { | |||
margin: 20px; | |||
background-color: white; | |||
border: 1px solid red; | |||
.task-search { | |||
margin: 20px; | |||
display: flex; | |||
flex-wrap: wrap; | |||
justify-content: space-between; | |||
.task-inp { | |||
width: 200px; | |||
margin-bottom: 20px; | |||
} | |||
.btns { | |||
.reset { | |||
margin-right: 20px; | |||
} | |||
} | |||
} | |||
.task-btns { | |||
margin: 20px; | |||
} | |||
:deep(.n-data-table-thead) { | |||
--n-merged-th-color: rgba(230, 247, 255, 1); | |||
} | |||
} | |||
</style> |