feat: 小程序移除管理后台入口,新增admin-web前端项目
将管理后台功能从微信小程序中剥离,独立为Vue.js前端项目admin-web Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
58
admin-web/src/components/ConfirmHost.vue
Normal file
58
admin-web/src/components/ConfirmHost.vue
Normal file
@@ -0,0 +1,58 @@
|
||||
<template>
|
||||
<transition name="fade">
|
||||
<div v-if="visible" class="modal-mask" @click.self="handleCancel">
|
||||
<div class="modal-card">
|
||||
<div class="modal-title">{{ title }}</div>
|
||||
<div class="modal-content">{{ content }}</div>
|
||||
<div class="btn-row">
|
||||
<button v-if="showCancel" class="btn btn-ghost" type="button" @click="handleCancel">{{ cancelText }}</button>
|
||||
<button class="btn btn-primary" type="button" @click="handleConfirm">{{ confirmText }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'ConfirmHost',
|
||||
data() {
|
||||
return {
|
||||
visible: false,
|
||||
title: '提示',
|
||||
content: '',
|
||||
confirmText: '确定',
|
||||
cancelText: '取消',
|
||||
showCancel: true,
|
||||
onConfirm: null,
|
||||
onCancel: null
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
open(opts) {
|
||||
this.title = opts.title || '提示'
|
||||
this.content = opts.content || ''
|
||||
this.confirmText = opts.confirmText || '确定'
|
||||
this.cancelText = opts.cancelText || '取消'
|
||||
this.showCancel = opts.showCancel !== false
|
||||
this.onConfirm = opts.onConfirm
|
||||
this.onCancel = opts.onCancel
|
||||
this.visible = true
|
||||
},
|
||||
handleConfirm() {
|
||||
this.visible = false
|
||||
const fn = this.onConfirm
|
||||
this.onConfirm = null
|
||||
this.onCancel = null
|
||||
if (typeof fn === 'function') fn()
|
||||
},
|
||||
handleCancel() {
|
||||
this.visible = false
|
||||
const fn = this.onCancel
|
||||
this.onConfirm = null
|
||||
this.onCancel = null
|
||||
if (typeof fn === 'function') fn()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
57
admin-web/src/components/ImagePreviewHost.vue
Normal file
57
admin-web/src/components/ImagePreviewHost.vue
Normal file
@@ -0,0 +1,57 @@
|
||||
<template>
|
||||
<transition name="fade">
|
||||
<div v-if="visible" class="preview-mask" @click.self="close">
|
||||
<div class="preview-stage">
|
||||
<div class="preview-close" @click="close">×</div>
|
||||
<div v-if="urls.length > 1" class="preview-nav preview-prev" @click="prev">‹</div>
|
||||
<img class="preview-img" :src="currentUrl" alt="preview" />
|
||||
<div v-if="urls.length > 1" class="preview-nav preview-next" @click="next">›</div>
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'ImagePreviewHost',
|
||||
data() {
|
||||
return {
|
||||
visible: false,
|
||||
urls: [],
|
||||
index: 0
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
currentUrl() {
|
||||
return this.urls[this.index] || ''
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
open({ urls, current }) {
|
||||
this.urls = Array.isArray(urls) ? urls.slice() : [urls]
|
||||
const idx = this.urls.indexOf(current)
|
||||
this.index = idx >= 0 ? idx : 0
|
||||
this.visible = true
|
||||
document.addEventListener('keydown', this.handleKey)
|
||||
},
|
||||
close() {
|
||||
this.visible = false
|
||||
document.removeEventListener('keydown', this.handleKey)
|
||||
},
|
||||
prev() {
|
||||
this.index = (this.index - 1 + this.urls.length) % this.urls.length
|
||||
},
|
||||
next() {
|
||||
this.index = (this.index + 1) % this.urls.length
|
||||
},
|
||||
handleKey(e) {
|
||||
if (e.key === 'Escape') this.close()
|
||||
else if (e.key === 'ArrowLeft') this.prev()
|
||||
else if (e.key === 'ArrowRight') this.next()
|
||||
}
|
||||
},
|
||||
beforeDestroy() {
|
||||
document.removeEventListener('keydown', this.handleKey)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
34
admin-web/src/components/ToastHost.vue
Normal file
34
admin-web/src/components/ToastHost.vue
Normal file
@@ -0,0 +1,34 @@
|
||||
<template>
|
||||
<div class="toast-host">
|
||||
<div
|
||||
v-for="item in list"
|
||||
:key="item.id"
|
||||
class="toast-item"
|
||||
:class="{ 'is-error': item.type === 'error', 'is-success': item.type === 'success' }"
|
||||
>
|
||||
{{ item.message }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
let uid = 0
|
||||
|
||||
export default {
|
||||
name: 'ToastHost',
|
||||
data() {
|
||||
return {
|
||||
list: []
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
push({ message, type = 'info', duration = 2000 }) {
|
||||
const id = ++uid
|
||||
this.list.push({ id, message, type })
|
||||
setTimeout(() => {
|
||||
this.list = this.list.filter((item) => item.id !== id)
|
||||
}, duration)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
Reference in New Issue
Block a user