二次元绘画创作
56.21M · 2026-02-04
<!-- 图片展示 -->
<template>
<el-main>
<div class="cateList1">
<el-row :gutter="20">
<el-col :span="6" v-for="(item, i) in dataList" :key="i">
<el-card style="margin-top: 15px; position: relative" shadow="hover">
<el-image
style="width: 100%; height: 150px"
:src="item.url"
fit="cover"
/>
<div class="pic_title">{{ item.name }}</div>
<div class="pic_edit">
<el-checkbox v-model="item.checked" @change="selectImg(item)" />
<span @click="openDialog(item)">重命名</span>
<span @click="delPic(item.id)">删除</span>
</div>
</el-card>
</el-col>
</el-row>
</div>
<div class="page1">
<el-pagination
v-model:current-page="querData.page"
v-model:page-size="querData.limit"
:page-sizes="[2, 8, 15, 20]"
small="small"
background="background"
layout="total, sizes, prev, pager, next, jumper"
:total="total"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</div>
<!-- 重命名 -->
<el-dialog v-model="dialogVisible" title="重命名" width="40%">
<el-form :model="formDate">
<el-form-item label="图片名称">
<el-input v-model="formDate.name" />
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="submitOk"> 确定 </el-button>
</span>
</template>
</el-dialog>
<!-- 上传图片 -->
<el-dialog v-model="dialogVisibleUpload" title="上传图片" width="40%">
<UploadImg
:data="{ image_class_id: querData.id }"
@success="uploadSuccess"
></UploadImg>
<template #footer>
<span class="dialog-footer">
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="dialogVisible = false">
确定
</el-button>
</span>
</template>
</el-dialog>
</el-main>
</template>
<script setup>
import { reactive, ref, computed } from 'vue'
import { getPicListByIdFn, editPicNameFn, delPicFn } from '@/api/pic.js'
import { ElMessage, ElMessageBox } from 'element-plus'
import UploadImg from '@/components/UploadImg.vue'
const props = defineProps({
num: {
type: Number,
default: 1,
},
})
const checkedImg = computed(() => {
return dataList.value.filter((item) => item.checked)
})
const emit = defineEmits('selectImgData')
//选择图片事件
const selectImg = (item) => {
console.log(item)
if (item.checked && checkedImg.value.length > props.num) {
item.checked = false
ElMessage.error(`只能选择${props.num}张图片`)
return
}
emit('selectImgData', checkedImg.value)
}
//上传图片对话框显示和隐藏
const dialogVisibleUpload = ref(false)
const openDialogUpload = () => {
dialogVisibleUpload.value = true
}
const uploadSuccess = () => {
getPicList()
}
//删除
const delPic = async (id) => {
const isdel = await ElMessageBox.confirm('是否删除?', '删除', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
}).catch((err) => err)
console.log(isdel)
if (isdel !== 'confirm') {
return
}
const res = await delPicFn([id])
console.log(res)
getPicList()
}
const dialogVisible = ref(false)
//打开对话框
const openDialog = (item) => {
console.log(item)
formDate.id = item.id
formDate.name = item.name
dialogVisible.value = true
}
const formDate = reactive({
id: 0,
name: '',
})
//确定修改图片名称
const submitOk = async () => {
const res = await editPicNameFn(formDate.id, formDate.name)
console.log(res)
if (res.msg && res.msg !== 'ok') {
return
}
dialogVisible.value = false
getPicList()
}
const querData = reactive({
//图库分类ID
id: 0,
page: 1,
limit: 8,
})
//分页
const handleSizeChange = (val) => {
console.log(val)
querData.limit = val
getPicList()
}
const handleCurrentChange = (val) => {
querData.page = val
getPicList()
}
const dataList = ref([])
const total = ref(0)
const getPicList = async () => {
const res = await getPicListByIdFn(querData.id, querData.page, querData.limit)
if (res.msg && res.msg !== 'ok') {
return
}
dataList.value = res.data.list.map((item) => {
item.checked = false
return item
})
console.log(dataList.value)
total.value = res.data.totalCount
}
//getPicList()
//获取父组件分类id
const getDataById = (id) => {
//id 分类id
querData.id = id
getPicList()
}
// 还需要通过defineExpose暴露据和方法让子组件许可和确认,接下来在父组件中就可以查看子组件中暴露的数据与方法
defineExpose({
getDataById,
openDialogUpload,
})
</script>
<style lang="less" scoped>
:deep(.el-card__body) {
padding: 0px !important;
}
</style>
import request from '@/utils/request.js'
export const getPicCateFn=(page,limit=10)=>{
return request({
url:`admin/getPicsCateList/${page}`,
method:'GET',
params:{
limit
}
})
}
//修改图库分类
export const editPicCateFn=(id,data)=>{
return request({
url:`admin/editPicsList/${id}`,
method:'POST',
data
})
}
//删除图库分类
export const delCateFn=(id)=>{
return request({
url:`admin/delPicsCateList/${id}/delete`,
method:'POST'
})
}
//获取图库分类
export const getPicListByIdFn=(id,page,limit)=>{
return request({
url:`admin/getPicList/${id}/image/${page}`,
method:'GET',
params:{
limit
}
})
}
//删除图片
export const delPicFn=(ids)=>{
return request({
url:"admin/delPic/delete_all",
method:'POST',
data:{
ids
}
})
}
// 修改密码
import { editPasswordFn } from '@/api/login.js'
import { ref, reactive } from 'vue'
import { ElMessage } from 'element-plus'
export function useEditPassword() {
//修改对话是否显示
const dialogVisible = ref(false)
//form表单dom元素
const ruleFormRef = ref(null)
//修改数据源
const ruleForm = reactive({
oldpassword: '',
password: '',
repassword: ''
})
//验证规则
const rules = reactive({
oldpassword: [
{ required: true, message: '请输入旧密码', trigger: 'blur' },
{ min: 5, max: 15, message: '请输入5到15个字符', trigger: 'blur' },
],
password: [
{ required: true, message: '请输入新密码', trigger: 'blur' },
{ min: 5, max: 15, message: '请输入5到15个字符', trigger: 'blur' },
],
})
//关闭对话框的回调
const closeHandle = () => {
ruleFormRef.value.resetFields()
}
//确定修改密码
const submitOk = () => {
ruleFormRef.value.validate(async isValid => {
console.log(isValid)
if (!isValid) {
return
}
const res = await editPasswordFn(ruleForm)
console.log(res)
if (res.msg && res.msg !== 'ok') {
return ElMessage.error(res.msg)
}
ElMessage({
message: '密码修改成功',
type: 'success',
})
dialogVisible.value = false
})
}
return {
dialogVisible,
ruleFormRef,
ruleForm,
rules,
closeHandle,
submitOk
}
}
import {createRouter,createWebHashHistory} from 'vue-router'
import store from '@/store/index.js'
import np from 'nprogress'
//路由匹配规则
const routes=[
{
path:'/',
redirect: '/home'
},
{
path:'/home',
component:()=>import('@/views/Home.vue'),
children:[
{
path:'/home',
name:'/home',
component:()=>import('@/views/HomeIndex.vue'),
meta:{
title:'后台首页'
}
},
{
path:'/notice/list',
name:'/notice/list',
component:()=>import('@/views/test.vue'),
meta:{
title:'公告管理'
}
}
]
},
{
path:'/login',
component:()=>import('@/views/Login.vue')
},
//捕获404
{
path:'/:pathMatch(.*)*',
name:'NotFound',
component:()=>import('@/views/404.vue')
}
]
const router=createRouter({
history:createWebHashHistory(),
routes
})
//全局路由守卫
router.beforeEach(async (to,from,next)=>{
np.start()
//获取token
const tokenStr=window.sessionStorage.getItem('token')
if(tokenStr){
await store.dispatch('getUserInfo')
}
//如果没有登录
if(!tokenStr&&to.path!=='/login') return next('/login')
//如果已经登录路
if(tokenStr&&to.path=='/login'){
return next({path:from.path?from.path:'/'})
}
next()
})
//后置全局路由守卫
router.afterEach((to,from,next)=>{
np.done()
})
export default router
import { createStore } from 'vuex'
import { getUserInfoFn } from '@/api/login'
const store = createStore({
state() {
return {
//管理员信息
userInfo: {
},
//导航菜单
menu:[],
//控制导航是否隐藏
isShow:false
}
},
mutations: {
setUserInfo(state, userInfo) {
state.userInfo = userInfo
state.menu=userInfo.menus
},
//修改isShow
setShow(state){
state.isShow=!state.isShow
}
},
actions: {
getUserInfo(context) {
return new Promise((resolve, reject) => {
getUserInfoFn().then(res => {
console.log(res)
context.commit('setUserInfo', res.data)
resolve(res)
}).catch(err => reject(err))
})
}
}
})
export default store
import { createApp } from 'vue'
import router from './router/index.js'
import './style.css'
import store from '@/store/index.js'
import 'nprogress/nprogress.css'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import App from './App.vue'
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
const app = createApp(App)
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
app.component(key, component)
}
app.use(ElementPlus)
app.use(router)
app.use(store)
app.mount('#app')
<template>
<div class="header">
<!-- 左列 -->
<span class="logo"> 商城后台管理系统 </span>
<el-icon class="icon" @click="setIsShow">
<Fold />
</el-icon>
<!-- 右列 -->
<div class="f_right">
<el-tooltip
effect="dark"
content="刷新"
:enterable="false"
placement="bottom"
>
<el-icon class="icon" @click="refHandle">
<Refresh />
</el-icon>
</el-tooltip>
<el-tooltip
v-if="!isFullscreen"
effect="dark"
content="全屏"
:enterable="false"
placement="bottom"
>
<el-icon class="icon" @click="toggle">
<FullScreen />
</el-icon>
</el-tooltip>
<el-tooltip
v-else
effect="dark"
content="退出全屏"
:enterable="false"
placement="bottom"
>
<el-icon class="icon" @click="toggle">
<FullScreen />
</el-icon>
</el-tooltip>
<el-dropdown @command="commandHandle">
<span>
<el-avatar :size="30" :src="$store.state.userInfo.avatar" />
{{ $store.state.userInfo.username }}
<el-icon class="el-icon--right" style="margin-left: 10px">
<arrow-down />
</el-icon>
</span>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item command="editPassword">修改密码</el-dropdown-item>
<el-dropdown-item command="logout">退出登录</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
<!-- 修改密码对话框 -->
<el-dialog
v-model="dialogVisible"
title="修改密码"
width="40%"
@close="closeHandle"
>
<el-form
ref="ruleFormRef"
:model="ruleForm"
:rules="rules"
label-width="100px"
>
<el-form-item label="原密码" prop="oldpassword">
<el-input v-model="ruleForm.oldpassword" />
</el-form-item>
<el-form-item label="新密码" prop="password">
<el-input v-model="ruleForm.password" />
</el-form-item>
<el-form-item label="确认密码" prop="repassword">
<el-input v-model="ruleForm.repassword" />
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="submitOk"> 确定 </el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script setup>
import { useEditPassword } from '@/utils/useEditPWP.js'
import { Fold, Refresh, FullScreen, ArrowDown } from '@element-plus/icons-vue'
import { useFullscreen } from '@vueuse/core'
import { useStore } from 'vuex'
import { useRouter } from 'vue-router'
import { ElMessageBox } from 'element-plus'
const store = useStore()
const router = useRouter()
const { toggle, isFullscreen } = useFullscreen()
const { dialogVisible, ruleFormRef, ruleForm, rules, closeHandle, submitOk } =
useEditPassword()
//刷新
const refHandle = () => {
location.reload()
}
//退出
const logout = async () => {
const isLogout = await ElMessageBox.confirm('是否退出?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
}).catch((err) => err)
console.log(isLogout)
if (isLogout !== 'confirm') {
return
}
//清空本地存储
window.sessionStorage.removeItem('token')
//清空vuex数据
store.commit('setUserInfo', {})
router.push({ path: '/login' })
}
//下拉菜单事件
const commandHandle = (res) => {
if (res == 'editPassword') {
console.log('修改密码')
dialogVisible.value = true
} else if (res == 'logout') {
console.log('退出')
logout()
}
}
//控制导航菜单的显示和隐藏
const setIsShow = () => {
store.commit('setShow')
}
</script>
<template>
<div>
<el-card>
<div class="e_title">
订单统计
<span>
<el-check-tag @change="selectOp(item.value)" :checked="ops == item.value" v-for="(item, i) in option"
:key="i">
{{ item.text }}
</el-check-tag>
</span>
</div>
<div id="e_main">
</div>
</el-card>
</div>
</template>
<script setup>
import * as echarts from 'echarts';
import { getEchartsFn } from '@/api/home.js'
import { ref, onMounted,onBeforeUnmount } from 'vue'
var myChart
onMounted(() => {
var chartDom = document.getElementById('e_main');
myChart = echarts.init(chartDom);
getEchartsData()
})
const getEchartsData=async ()=>{
var option;
option = {
xAxis: {
type: 'category',
data: []
},
yAxis: {
type: 'value'
},
series: [
{
data: [],
type: 'bar',
showBackground: true,
backgroundStyle: {
color: 'rgba(180, 180, 180, 0.2)'
}
}
]
};
myChart.showLoading()
const res=await getEchartsFn(ops.value)
myChart.hideLoading()
console.log(res)
if(res.msg&&res.msg!=='ok'){
return
}
option.xAxis.data=res.data.x
option.series[0].data=res.data.y
option && myChart.setOption(option);
}
const ops = ref('week')
const option = [
{
text: '月',
value: 'month'
},
{
text: '周',
value: 'week'
},
{
text: '天',
value: 'hour'
}
]
//选择时间事件
const selectOp = (op) => {
ops.value = op
getEchartsData()
}
//销毁期间生命周期
onBeforeUnmount(()=>{
if(myChart){
echarts.dispose(myChart)
}
})
const props=defineProps({
value:{
type:Number,
default:0
}
})
const data=reactive({
num:0
})
function fn(){
gsap.to(data,{
//0.5毫秒之后 将初始数据 动画替换成父组件传递过来的数据
duration:0.5,
num:props.value
})
}
fn()
watch(()=>props.value,()=>fn())
<el-form-item label="菜单图标" v-if="formData.menu == 1">
<IconSelect v-model="formData.icon"></IconSelect>
</el-form-item>
//接收v-model的值
defineProps({
modelValue: String
})
const emit = defineEmits(['update:modelValue'])
//选择图标事件
const changeHandle = (e) => {
console.log(e)
emit("update:modelValue", e)
}
const dialogVisible = ref(false)
const titleVal = ref('新增')
const skusId = ref(0)
//新增参数
const formData = reactive({
name: '',
default: '',
order: 50,
status: 1
})
const openDialog = () => {
titleVal.value = '新增'
formData.name = ''
formData.default = ''
formData.order = 50
formData.status = 1
dialogVisibl
</script>