zhangtao 1 год назад
Родитель
Сommit
2552eada6f
9 измененных файлов: 640 добавлений и 12 удалений
  1. +60
    -0
      package-lock.json
  2. +47
    -0
      src/api/system/banner.js
  3. +3
    -0
      src/main.js
  4. +5
    -2
      src/utils/ui/theme.js
  5. +158
    -0
      src/views/system-manage/banner-manage/BannerModal.vue
  6. +47
    -1
      src/views/system-manage/banner-manage/index.vue
  7. +19
    -0
      src/views/system-manage/banner-manage/tool/search.js
  8. +133
    -0
      src/views/system-manage/banner-manage/tool/table.js
  9. +168
    -9
      src/views/task-manage/index.vue

+ 60
- 0
package-lock.json Просмотреть файл

@@ -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",

+ 47
- 0
src/api/system/banner.js Просмотреть файл

@@ -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'
})
}

+ 3
- 0
src/main.js Просмотреть файл

@@ -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() {

+ 5
- 2
src/utils/ui/theme.js Просмотреть файл

@@ -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 = {

+ 158
- 0
src/views/system-manage/banner-manage/BannerModal.vue Просмотреть файл

@@ -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>

+ 47
- 1
src/views/system-manage/banner-manage/index.vue Просмотреть файл

@@ -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
}
}
}


+ 19
- 0
src/views/system-manage/banner-manage/tool/search.js Просмотреть файл

@@ -0,0 +1,19 @@
import { reactive } from 'vue'
export const search = reactive({
search: [
{
label: '用户账号',
key: 'username',
props: {
placeholder: '请输入用户账号'
}
},
{
label: '用户姓名',
key: 'realname',
props: {
placeholder: '请输入用户姓名'
}
}
]
})

+ 133
- 0
src/views/system-manage/banner-manage/tool/table.js Просмотреть файл

@@ -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

+ 168
- 9
src/views/task-manage/index.vue Просмотреть файл

@@ -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>

Загрузка…
Отмена
Сохранить