crud grid

master
veypi 1 year ago
parent 2dda72bbf3
commit 8a0b44fb96

@ -197,8 +197,8 @@ pub fn init_log() {
tracing_subscriber::fmt()
.with_line_number(true)
.with_timer(FormatTime {})
.with_max_level(Level::TRACE)
.with_target(false)
.with_file(true)
.with_max_level(Level::INFO)
// .with_target(false)
// .with_file(true)
.init();
}

@ -69,6 +69,7 @@ module.exports = {
// add your custom rules here
rules: {
'@typescript-eslint/ban-ts-comment': 0,
'@typescript-eslint/no-empty-function': 0,
'vue/multi-word-component-names': 0,
'@typescript-eslint/no-unused-vars': 0,

@ -35,7 +35,11 @@ onBeforeMount(() => {
html,
body,
#q-app {
@apply font-mono h-full w-full select-none;
@apply font-mono h-full w-full;
}
:root {
--z-index: 1;
}
.page-h1 {

@ -27,9 +27,9 @@ conf.timeout = 5000
Cfg.host.value = 'http://' + window.location.host
Cfg.uuid.value = 'FR9P5t8debxc11aFF'
evt.on('token', (t) => {
oafs.setCfg({ token: util.getToken() })
Cfg.token.value = util.getToken()
evt.on('token', (t: any) => {
oafs.setCfg({ token: t })
Cfg.token.value = t
})
// "async" is optional;

@ -0,0 +1,90 @@
<!--
* crud.vue
* Copyright (C) 2023 veypi <i@veypi.com>
* 2023-10-10 19:29
* Distributed under terms of the MIT license.
-->
<template>
<div>
<q-page-sticky position="top-right" style="z-index: 20" :offset="[27, 27]">
<q-btn @click="modeV = !modeV" round icon="save_as" class="" />
</q-page-sticky>
<div class="grid" :class="[modeV ? '' : 'grid-cols-2']">
<div class="grid" :style="modeV ? grid_len : ''">
<div :class="styles.k" v-for="k of keys" :key="k.name">
{{ k.label || k.name }}
</div>
</div>
<div class="grid" :style="modeV ? '' : grid_len">
<div class="grid hover:bg-gray-200" :style="modeV ? grid_len : ''" v-for="( item, idx ) in data " :key="idx">
<div :class="styles.v" v-for="k of keys" :key="k.name">
{{ item[k.name] }}
</div>
</div>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { computed, ref, watch } from 'vue';
interface itemProp {
name: '',
label?: '',
value: any,
type?: '',
}
interface keyProp {
name: string,
label?: string,
default?: any,
typ?: string,
}
const grid_len = computed(() => {
return {
'grid-template-columns': 'repeat(' +
(modeV.value ? props.keys?.length : props.data?.length)
+ ', minmax(0, 1fr))'
}
})
let props = withDefaults(defineProps<{
vertical?: boolean
keys?: keyProp[],
data?: any[]
kclass?: Array<string>,
vclass?: Array<string>,
cclass?: Array<string>,
}>(),
{
vertical: false,
data: [] as any,
kclass: [] as any,
vclass: [] as any,
cclass: ['w-40', 'h-40'] as any,
}
)
const styles = computed(() => {
let k = [];
let v = [];
k.push(...props.kclass, ...props.cclass)
v.push(...props.vclass, ...props.cclass)
return {
k, v
}
})
const modeV = ref(props.vertical)
watch(computed(() => props.vertical), (v) => modeV.value = v)
</script>
<style scoped></style>

@ -20,27 +20,22 @@ import oafs from 'src/libs/oafs';
let editor = {} as Cherry;
let emits = defineEmits<{
(e: 'updated', v: string): void
(e: 'save', v: string): void
(e: 'update:modelValue', v: boolean): void
}>()
let props = withDefaults(defineProps<{
modelValue: boolean,
eid?: string,
content?: string,
preview?: boolean,
static_dir?: string,
}>(),
{
eid: 'v-editor',
content: '',
preview: false,
}
)
watch(computed(() => props.preview), (e) => {
if (e) {
let des = editor.getValue()
console.log(des)
emits('updated', des)
}
watch(computed(() => props.modelValue), (e) => {
set_mode(e)
})
watch(computed(() => props.content), (e) => {
@ -70,19 +65,36 @@ const fileUpload = (f: File, cb: (url: string, params: any) => void) => {
})
})
}
const saveMenu = Cherry.createMenuHook('保存', {
onClick: function () {
let des = editor.getValue()
emits('save', des)
return
}
});
const backMenu = Cherry.createMenuHook('返回', {
onClick: function () {
emits('update:modelValue', true)
return
}
})
const init = () => {
let config = {
value: props.content,
id: props.eid,
// isPreviewOnly: props.preview,
callback: {
},
fileUpload: fileUpload,
} as CherryOptions;
config.callback.afterInit = () => {
}
};
// @ts-ignore
options.toolbars.customMenu.saveMenu = saveMenu
// @ts-ignore
options.toolbars.customMenu.backMenu = backMenu
editor = new Cherry(Object.assign({}, options, config));
set_mode(props.preview)
set_mode(props.modelValue)
}

@ -7,6 +7,7 @@
import { CherryOptions } from 'cherry-markdown/types/cherry';
const basicConfig: CherryOptions = {
id: '',
value: '',
@ -57,10 +58,10 @@ const basicConfig: CherryOptions = {
},
onClickPreview: () => { },
onCopyCode: (e: ClipboardEvent, code: string) => code,
changeString2Pinyin: (s) => s,
changeString2Pinyin: (s: any) => s,
},
isPreviewOnly: false,
fileUpload: (f) => { console.log('upload file: ' + f) },
fileUpload: (f: any) => { console.log('upload file: ' + f) },
fileTypeLimitMap: {
video: "",
audio: "",
@ -72,18 +73,49 @@ const basicConfig: CherryOptions = {
openai: false,
engine: {
global: {
urlProcessor(url, srcType) {
urlProcessor(url: any, srcType: any) {
// console.log(`url-processor`, url, srcType);
return url;
},
},
syntax: {
codeBlock: {
theme: 'twilight',
autoLink: {
/** default open short link display */
enableShortLink: true,
/** default display 20 characters */
shortLinkLength: 20,
},
list: {
listNested: false, // The sibling list type becomes a child after conversion
indentSpace: 2, // Default 2 space indents
},
table: {
enableChart: false,
// chartEngine: Engine Class
// chartRenderEngine: EChartsTableEngine,
// externals: ['echarts'],
},
inlineCode: {
theme: 'red',
},
codeBlock: {
theme: 'twilight', // Default to dark theme
wrap: true, // If it exceeds the length, whether to wrap the line. If false, the scroll bar will be displayed
lineNumber: true, // Default display line number
customRenderer: {
// Custom syntax renderer
},
/**
* indentedCodeBlock Is the switch whether indent code block is enabled
*
* this syntax is not supported by default in versions before 6.X.
* Because cherry's development team thinks the syntax is too ugly (easy to touch by mistake)
* The development team hopes to completely replace this syntax with ` ` code block syntax
* However, in the subsequent communication, the development team found that the syntax had better display effect in some scenarios
* Therefore, the development team in 6 This syntax was introduced in version X
* if you want to upgrade the following versions of services without users' awareness, you can remove this syntax:
* indentedCodeBlockfalse
*/
indentedCodeBlock: true,
},
fontEmphasis: {
allowWhitespace: false, // 是否允许首尾空格
@ -95,6 +127,7 @@ const basicConfig: CherryOptions = {
engine: 'MathJax', // katex或MathJax
// src: 'https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-svg.js', // 如果使用MathJax plugins则需要使用该url通过script标签引入
src: '/deps/mathjax/tex-svg.js',
plugins: true,
},
inlineMath: {
engine: 'MathJax', // katex或MathJax
@ -104,6 +137,19 @@ const basicConfig: CherryOptions = {
// customResourceURL: 'https://github.githubassets.com/images/icons/emoji/unicode/${code}.png?v8',
upperCase: true,
},
toc: {
/** By default, only one directory is rendered */
allowMultiToc: false,
},
header: {
/**
* Style of title
* - default Default style with anchor in front of title
* - autonumber There is a self incrementing sequence number anchor in front of the title
* - none Title has no anchor
*/
anchorStyle: 'autonumber',
},
// toc: {
// tocStyle: 'nested'
// }
@ -142,11 +188,15 @@ const basicConfig: CherryOptions = {
'graph',
'togglePreview',
'export',
'saveMenu',
'backMenu'
],
// toolbarRight: [],
bubble: ['bold', 'italic', 'underline', 'strikethrough', 'sub', 'sup', 'quote', 'ruby', '|', 'size', 'color'], // array or false
// sidebar: false,
// float: false
customMenu: {
} as any,
},
drawioIframeUrl: '/cherry/drawio.html',
editor: {

@ -12,14 +12,17 @@
// to match your app's branding.
// Tip: Use the "Theme Builder" on Quasar's documentation website.
$primary : #1976D2;
$secondary : #26A69A;
// src/css/quasar.variables.sass
$primary : #f74d22;
$secondary : #fa9243;
$accent : #9C27B0;
$dark : #1D1D1D;
$dark : #1d1d1d;
$dark-page : #121212;
$positive : #21BA45;
$negative : #C10015;
$info : #31CCEC;
$warning : #F2C037;
$negative : #ff0000;
$info : #28d4ce;
$warning : #f2e638;

@ -4,7 +4,7 @@
<q-toolbar class="pl-0">
<q-toolbar-title class="flex items-center cursor-pointer" @click="router.push({ name: 'home' })">
<q-icon size="3rem" class="mx-1" color="aqua" name='v-glassdoor' style="color: aqua;"></q-icon>
<q-icon size="3rem" class="mx-1" color="#0ff" name='v-glassdoor' style="color: aqua;"></q-icon>
<q-separator dark vertical inset />
<span class="ml-3">
统一认证系统
@ -42,9 +42,9 @@
</router-view>
</q-page>
</q-page-container>
<q-footer bordered class="bg-grey-8 text-white flex justify-around">
<span class="hover:text-black cursor-pointer" @click="$router.push({ name: 'about' })">关于OA</span>
<span class="hover:text-black cursor-pointer">使用须知</span>
<q-footer style="z-index: 1;" bordered class="bg-grey-8 text-white flex justify-around">
<span class="hover:text-black cursor-pointer" @click="$router.push({ name: 'doc' })">关于OA</span>
<span class="hover:text-black cursor-pointer" @click="$router.push({ name: 'doc' })">使用须知</span>
<span class="hover:text-black cursor-pointer" @click="util.goto('https://veypi.com')">
©2021 veypi
</span>

@ -42,8 +42,8 @@ const util = {
return localStorage.getItem('auth_token') || ''
},
setToken(t: string) {
evt.emit('token', t)
localStorage.setItem('auth_token', t)
evt.emit('token', t)
},
addTokenOf(url: string) {
return url + '?auth_token=' + encodeURIComponent(this.getToken())

@ -0,0 +1,37 @@
<!--
* AppCfg.vue
* Copyright (C) 2023 veypi <i@veypi.com>
* 2023-10-10 16:08
* Distributed under terms of the MIT license.
-->
<template>
<div>
<div>
<CRUD :keys="keys" :data="data">
<template #a="">_____</template>
</CRUD>
</div>
</div>
</template>
<script lang="ts" setup>
import CRUD from 'src/components/crud.vue'
import { ref } from 'vue';
const keys = ref<any>([
{
name: 'name',
label: '阿萨德',
}, { name: 'key' }, { name: 'status' }
])
const data = ref([
{ name: 'asd', key: 'akk' },
{ name: '1sd', key: '1kk' },
{ name: '2sd', key: '2kk' },
{ name: '3sd', key: '3kk' },
{ name: '4sd', key: '4kk' },
])
</script>
<style scoped></style>

@ -6,13 +6,10 @@
-->
<template>
<div>
<q-page-sticky position="top-right" :offset="[27, 27]">
<q-btn @click="sync_editor" :style="{
color: edit_mode ? 'red' :
''
}" round icon="save_as" class="" />
<q-page-sticky position="top-right" style="z-index: 20" :offset="[27, 27]">
<q-btn v-if="preview_mode" @click="preview_mode = false" round icon="save_as" class="" />
</q-page-sticky>
<Editor v-if="app.id" :eid="app.id + '.des'" :preview="!edit_mode" :content="content" @updated="save"></Editor>
<Editor v-if="app.id" :eid="app.id + '.des'" v-model="preview_mode" :content="content" @save="save"></Editor>
</div>
</template>
@ -27,7 +24,7 @@ import oafs from 'src/libs/oafs';
let edit_mode = ref(false)
let preview_mode = ref(true)
let app = inject('app') as Ref<modelsApp>
let content = ref()
@ -46,7 +43,7 @@ const save = (des: string) => {
let a = new File([des], app.value.name + '.md');
oafs.upload([a], app.value.id).then(url => {
api.app.update(app.value.id, { des: url[0] }).then(e => {
edit_mode.value = false
preview_mode.value = true
app.value.des = url[0]
}).catch(e => {
msg.Warn("更新失败: " + e)
@ -57,12 +54,6 @@ const save = (des: string) => {
}
const sync_editor = () => {
edit_mode.value = !edit_mode.value
}
onMounted(() => {
sync()
})

@ -2,8 +2,9 @@
<div class="flex items-center justify-center">
<div class="px-10 pb-9 pt-28 rounded-xl w-96">
<q-form autofocus @submit="onSubmit" @reset="onReset">
<q-input v-model="data.username" label="用户名" hint="username" lazy-rules :rules="data_rules.username" />
<q-input v-model="data.password" :type="isPwd ? 'password' :
<q-input v-model="data.username" autocomplete="username" label="用户名" hint="username" lazy-rules
:rules="data_rules.username" />
<q-input autocomplete="current-password" v-model="data.password" :type="isPwd ? 'password' :
'text'" hint="password" :rules="data_rules.password">
<template v-slot:append>
<q-icon :name="isPwd ? 'visibility_off' : 'visibility'" class="cursor-pointer" @click="isPwd = !isPwd" />
@ -27,7 +28,7 @@ import msg from '@veypi/msg'
import util from 'src/libs/util'
import { useUserStore } from 'src/stores/user'
import { useAppStore } from 'src/stores/app'
import { modelsApp } from 'src/models'
import { AUStatus, modelsApp } from 'src/models'
const app = useAppStore()
@ -60,6 +61,12 @@ const onSubmit = () => {
let url = route.query.redirect || data.redirect || '/'
redirect(url)
console.log(data)
}).catch(e => {
console.log([e])
let m = e === '1' ? '被禁止登录' : e === '2' ? '正在申请中' : e
=== '3' ?
'申请被拒绝' : '登录失败:' + e
msg.Warn(m)
})
}
const onReset = () => {

@ -2,9 +2,12 @@
<div class="flex items-center justify-center">
<div class="px-10 pb-9 pt-28 rounded-xl w-96">
<q-form @submit="register" autofocus>
<q-input v-model="data.username" label="用户名" hint="username" lazy-rules :rules="rules.username"></q-input>
<q-input label="密码" v-model="data.password" type="password" lazy-rules :rules="rules.password"></q-input>
<q-input label="密码" v-model="data.pass" type="password" lazy-rules :rules="rules.pass"></q-input>
<q-input autocomplete="username" v-model="data.username" label="用户名" hint="username" lazy-rules
:rules="rules.username"></q-input>
<q-input autocomplete="new-password" label="密码" v-model="data.password" type="password" lazy-rules
:rules="rules.password"></q-input>
<q-input autocomplete="new-password" label="密码" v-model="data.pass" type="password" lazy-rules
:rules="rules.pass"></q-input>
<div class="flex justify-around mt-4">
<q-btn label="注册" type="submit" color="primary" />
</div>

@ -46,7 +46,7 @@ const routes: RouteRecordRaw[] = [
loadcomponents('home', 'app.home', 'AppHome'),
loadcomponents('user', 'app.user', 'AppUser'),
loadcomponents('auth', 'app.auth', 'AppAuth'),
loadcomponents('settings', 'app.settings', 'IndexPage'),
loadcomponents('settings', 'app.settings', 'AppCfg'),
]
}
],

Loading…
Cancel
Save