first commit

This commit is contained in:
JaguarJack
2022-12-05 23:01:12 +08:00
commit 0024080c28
322 changed files with 27698 additions and 0 deletions

View File

@@ -0,0 +1,27 @@
<template>
<div :style="bgColor" class="flex flex-col w-full">
<img src="/admin/assets/404.png" class="w-full sm:w-3/5 m-auto" />
<div class="mr-auto w-full bottom-0 m-auto">
<div class="w-full text-center text-base text-gray-400">抱歉您访问的页面不存在</div>
<div @click="push('/')" class="text-center w-full mt-2">
<el-button type="primary">回到首页</el-button>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { useRouter } from 'vue-router'
import { useAppStore } from '/admin/stores/modules/app'
import { computed } from 'vue'
const { push } = useRouter()
const dark: string = '#161d31;'
const light: string = 'rgb(241,245,249);'
const appStore = useAppStore()
const bgColor = computed(() => {
return 'background-color:' + (appStore.getIsDarkMode ? dark : light)
})
</script>

View File

@@ -0,0 +1,97 @@
<template>
<div>
<el-table :data="tableData" style="width: 100%">
<el-table-column prop="date" label="Date" width="180" />
<el-table-column prop="name" label="Name" width="180" />
<el-table-column prop="address" label="Address" />
</el-table>
<el-button text @click="visible = true">Click to open the Dialog</el-button>
<ct-dialog v-model="visible" title="show me the code" :show-footer="true">
<el-form :model="form">
<el-form-item label="Promotion name" :label-width="formLabelWidth">
<el-input v-model="form.name" autocomplete="off" />
</el-form-item>
<el-form-item label="Zones" :label-width="formLabelWidth">
<el-select v-model="form.region" placeholder="Please select a zone">
<el-option label="Zone No.1" value="shanghai" />
<el-option label="Zone No.2" value="beijing" />
</el-select>
</el-form-item>
</el-form>
</ct-dialog>
<el-tabs v-model="activeName" type="border-card" class="demo-tabs" @tab-click="handleClick">
<el-tab-pane label="User" name="first">User</el-tab-pane>
<el-tab-pane label="Config" name="second">Config</el-tab-pane>
<el-tab-pane label="Role" name="third">Role</el-tab-pane>
<el-tab-pane label="Task" name="fourth">Task</el-tab-pane>
</el-tabs>
</div>
</template>
<script lang="ts" setup>
import CtDialog from './admin/Dialog.vue'
import { ref, reactive } from 'vue'
import type { TabsPaneContext } from 'element-plus'
const visible = ref(false)
const formLabelWidth = '140px'
const activeName = ref('first')
const handleClick = (tab: TabsPaneContext, event: Event) => {
console.log(tab, event)
}
const form = reactive({
name: '',
region: '',
date1: '',
date2: '',
delivery: false,
type: [],
resource: '',
desc: ''
})
const tableData = [
{
date: '2016-05-03',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles'
},
{
date: '2016-05-02',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles'
},
{
date: '2016-05-04',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles'
},
{
date: '2016-05-01',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles'
},
{
date: '2016-05-03',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles'
},
{
date: '2016-05-02',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles'
},
{
date: '2016-05-04',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles'
}
]
</script>
<style scoped>
.dialog-footer button:first-child {
margin-right: 10px;
}
</style>

View File

@@ -0,0 +1,49 @@
<template>
<el-select v-bind="$attrs">
<el-option-group v-for="group in elOptions" :key="group.label" :label="group.label" v-if="group">
<el-option v-for="item in group.options" :key="item.value" :label="item.label" :value="item.value" />
</el-option-group>
<el-option v-for="option in elOptions" :key="option.value" :label="option.label" :value="option.value" v-else>
<slot />
</el-option>
</el-select>
</template>
<script lang="ts" setup>
import http from '/admin/support/http'
import { ref } from 'vue'
const props = defineProps({
options: {
type: Array,
default: [],
},
api: {
type: String,
default: '',
},
group: {
type: Boolean,
default: false,
},
})
interface Option {
label: string
value: string | number
}
interface GroupOption {
label: string
options: Array<Option>
}
const elOptions = props.group ? ref<Array<GroupOption>>() : ref<Array<Option>>()
if (props.api) {
http.get('options/' + props.api).then(r => {
elOptions.value = r.data.data
})
} else {
elOptions.value = props.options
}
</script>

View File

@@ -0,0 +1,16 @@
<template>
<el-button type="primary" :size="size"><Icon name="plus" class="w-4 mr-1" /> {{ text }}</el-button>
</template>
<script lang="ts" setup>
defineProps({
size: {
type: String,
default: 'default',
},
text: {
type: String,
default: '新增',
},
})
</script>

View File

@@ -0,0 +1,16 @@
<template>
<el-button type="danger" :size="size"><Icon name="trash" class="w-4 mr-1" /> {{ text }}</el-button>
</template>
<script lang="ts" setup>
defineProps({
size: {
type: String,
default: 'small',
},
text: {
type: String,
default: '删除',
},
})
</script>

View File

@@ -0,0 +1,16 @@
<template>
<el-button type="primary" :size="size"><Icon name="eye" class="w-4 mr-1" /> {{ text }}</el-button>
</template>
<script lang="ts" setup>
defineProps({
size: {
type: String,
default: 'small',
},
text: {
type: String,
default: '详情',
},
})
</script>

View File

@@ -0,0 +1,18 @@
<template>
<el-button type="success" :size="size"><Icon name="pencil-square" class="w-4 mr-1" /> {{ text }}</el-button>
</template>
<script lang="ts" setup>
defineProps({
size: {
type: String,
default: 'small',
},
text: {
type: String,
default: '更新',
},
})
</script>
<style scoped></style>

View File

@@ -0,0 +1,100 @@
<template>
<div>
<el-dialog :model-value="modelValue" :show-close="false" :fullscreen="isFullscreen" v-bind="$attrs" :width="width" :close="close" :before-close="beforeClose" draggable>
<template #header="{ titleId, titleClass }">
<div class="flex justify-between w-full">
<div>
<h4 :id="titleId" :class="titleClass">{{ title }}</h4>
</div>
<div class="flex w-14 justify-between">
<Icon :name="fullscreenIcon" @click="fullscreen" class="hover:cursor-pointer" />
<Icon name="x-mark" class="hover:cursor-pointer" @click="close" />
</div>
</div>
</template>
<slot />
<template #footer v-if="showFooter">
<span class="dialog-footer">
<el-button @click="close">{{ $t('system.cancel') }}</el-button>
<el-button type="primary" @click="close">{{ $t('system.confirm') }}</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script lang="ts" setup>
import { ref, computed, onMounted } from 'vue'
const props = defineProps({
modelValue: {
type: Boolean,
default: false,
require: true,
},
showFooter: {
type: Boolean,
default: false,
},
width: {
type: String,
required: false,
default: '',
},
title: {
type: String,
default: '',
},
})
const emits = defineEmits(['update:modelValue'])
const isFullscreen = ref(false)
const fullscreenIcon = computed(() => {
return isFullscreen.value ? 'arrows-pointing-in' : 'arrows-pointing-out'
})
const fullscreen = () => {
isFullscreen.value = !isFullscreen.value
}
const close = () => {
emits('update:modelValue', false)
}
// 遮罩关闭调用
const beforeClose = () => {
emits('update:modelValue', false)
}
const width = ref<string>('')
onMounted(() => {
width.value = props.width ? props.width : getWidth()
})
// 窗口尺寸
const getWidth = () => {
const clientWidth = window.document.body.clientWidth
if (clientWidth <= 726) {
return '100%'
}
if (clientWidth > 726 && clientWidth < 1440) {
return '60%'
}
return '650px'
}
</script>
<style scoped lang="scss">
:deep(.el-dialog) {
border-radius: 0.5rem;
.el-dialog__header {
margin-right: 0 !important;
border-bottom: 1px solid #e2e8f0;
}
}
</style>

View File

@@ -0,0 +1,24 @@
<template>
<el-switch @change="enabled(api, id)" :active-value="Status.ENABLE" :inactive-value="Status.DISABLE" :model-value="modelValue" :loading="loading" />
</template>
<script lang="ts" setup>
import { useEnabled } from '/admin/composables/curd/useEnabled'
import { Status } from '/admin/enum/app'
import { watch } from 'vue'
const props = defineProps({
modelValue: Boolean | Number | String,
api: String,
id: Number | String,
})
const emits = defineEmits(['update:modelValue'])
const { enabled, success, loading } = useEnabled()
watch(success, function () {
emits('update:modelValue', props.modelValue === Status.ENABLE ? Status.DISABLE : Status.ENABLE)
success.value = false
})
</script>

View File

@@ -0,0 +1,59 @@
<template>
<el-breadcrumb separator="/" class="flex">
<transition-group name="breadcrumb">
<!--<el-breadcrumb-item :to="{ path: '/' }" class="text-blue=">Dashboard</el-breadcrumb-item>-->
<el-breadcrumb-item v-for="(item, index) in breadcrumbs" :key="index" class="text">{{ item }}</el-breadcrumb-item>
</transition-group>
</el-breadcrumb>
</template>
<script lang="ts" setup>
import router from '/admin/router'
import { watch, onMounted, ref } from 'vue'
import { useAppStore } from '/admin/stores/modules/app'
import { RouteLocationNormalizedLoaded } from 'vue-router'
const appStore = useAppStore()
const breadcrumbs = ref<string[]>([])
// 监听当前路由的变化
watch(router.currentRoute, (newValue, oldValue) => {
// 如果是内页,则不切换激活菜单
if (newValue.meta.is_inner === undefined || !newValue.meta.is_inner) {
appStore.setActiveMenu(newValue.path)
}
getBreadcrumbs(newValue)
})
// get init breadcrumb
onMounted(() => {
if (router.currentRoute.value.path !== '/') {
appStore.setActiveMenu(router.currentRoute.value.path)
}
getBreadcrumbs(router.currentRoute.value)
})
// get breadcrums
function getBreadcrumbs(newRoute: RouteLocationNormalizedLoaded) {
breadcrumbs.value = []
breadcrumbs.value.push('首页')
newRoute.matched.forEach(m => {
if (m.meta.title !== undefined) {
breadcrumbs.value.push(m.meta?.title as string)
}
})
}
</script>
<style>
.breadcrumb-leave-active {
transition: all 1s linear;
}
.breadcrumb-leave-to {
opacity: 0;
transition: all 0.3s linear;
}
</style>

View File

@@ -0,0 +1,23 @@
<template>
<component :is="icon" class="w-5 h-5" />
</template>
<script setup>
import { computed } from 'vue'
import * as heroIcons from '@heroicons/vue/24/outline'
const props = defineProps({
name: {
type: String,
required: true,
},
})
const icon = computed(() => {
let name = ''
props.name.split('-').forEach(v => {
name += v[0].toUpperCase() + v.substr(1)
})
return heroIcons[name + 'Icon']
})
</script>