Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

1st round refactor #42

Merged
merged 12 commits into from
May 30, 2023
Merged
846 changes: 0 additions & 846 deletions src/components/OrgTree.vue

This file was deleted.

21 changes: 21 additions & 0 deletions src/components/application/type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
export interface Application {
/** 应用 ID */
id: string;
/** 应用名称 */
name?: string;
/** 应用描述 */
description?: string;
/** 应用密钥 */
secret_key?: string;
}

export interface ApplicationPostData {
/** 应用名称 */
name?: string;
/** 应用描述 */
description?: string;
}

export interface ApplicationPostError {
name?: string;
}
56 changes: 56 additions & 0 deletions src/components/common/BooleanChip.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<template>
<q-chip
square
size="12px"
:label="value ? `${trueLabel}` : `${falseLabel}`"
class="text-weight-bold q-pa-sm q-ml-none"
:class="value ? 'chip-status-on' : 'chip-status-off'"
/>
</template>

<script lang="ts">
import { defineComponent } from 'vue';

export default defineComponent({
name: 'BooleanChip',

props: {
value: {
type: Boolean,
default: false,
},
trueLabel: {
type: String,
default: 'YES',
},
falseLabel: {
type: String,
default: 'NO',
},
},
});
</script>

<style lang="scss" scoped>
.chip-status-on {
background-color: $blue-1;
color: $blue-14;
}

.chip-status-off {
background-color: $red-1;
color: $red-8;
}

.body--dark {
.chip-status-on {
background-color: rgb(54, 72, 113);
color: $blue-1;
}

.chip-status-off {
background-color: #6f3a3a;
color: $red-1;
}
}
</style>
83 changes: 83 additions & 0 deletions src/components/common/ChipGroup.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
<template>
<q-chip
v-for="(chip, idx) in chips"
:key="idx"
size="12px"
:color="selectedChips.includes(chip.id) ? 'primary' : 'secondary'"
:text-color="selectedChips.includes(chip.id) ? 'white' : ''"
class="q-ml-none"
:clickable="clickable"
:square="square"
@click="clickChip(chip)"
>
<span class="material-icons-outlined q-pr-xs">
{{ icon }}
</span>
{{ chip.name }}
</q-chip>
</template>

<script lang="ts">
import { defineComponent, PropType, ref } from 'vue';

import { ChipGroupItem } from './type';

export default defineComponent({
name: 'ChipChipGroup',

props: {
chips: {
type: Array as PropType<ChipGroupItem[]>,
default: () => {
return [];
},
},
clickable: {
type: Boolean,
default: false,
},
square: {
type: Boolean,
default: false,
},
icon: {
type: String,
default: '',
},
selection: {
type: String,
default: 'single',
},
},

emits: ['selectedChange'],

setup() {
return {
selectedChips: ref<string[]>([]),
};
},

methods: {
clickChip(chip: ChipGroupItem) {
if (!this.clickable) return;
if (this.selection === 'multiple') {
if (this.selectedChips.includes(chip.id)) {
this.selectedChips = this.selectedChips.filter(
(tid) => tid != chip.id
);
} else {
this.selectedChips.push(chip.id);
}
} else if (this.selection === 'single') {
this.selectedChips = this.selectedChips.includes(chip.id)
? []
: [chip.id];
}
this.$emit('selectedChange', this.selectedChips);
},
},
});
</script>

<style lang="scss" scoped></style>
Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@
<template>
<q-item-label class="text-caption hint-label">
{{ name }}
{{ text }}
<q-icon
v-if="required"
name="emergency"
size="8px"
color="negative"
style="vertical-align: top"
class="assistant-icon"
/>
<q-icon
v-if="hint"
name="error_outline"
size="14px"
class="q-ml-xs"
style="vertical-align: top"
class="q-ml-xs assistant-icon"
>
<q-tooltip anchor="center right" self="center start">
{{ hint }}
Expand All @@ -29,7 +28,7 @@ export default defineComponent({
name: 'FieldLabel',

props: {
name: {
text: {
type: String,
default: '',
},
Expand All @@ -45,4 +44,8 @@ export default defineComponent({
});
</script>

<style lang="scss" scoped></style>
<style lang="scss" scoped>
.assistant-icon {
vertical-align: top;
}
</style>
5 changes: 5 additions & 0 deletions src/components/type.ts → src/components/common/type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,8 @@ export interface MenuButton {
/** 禁用按钮提示信息 */
disableHint?: string;
}

export interface ChipGroupItem {
id: string;
name: string;
}
8 changes: 6 additions & 2 deletions src/components/form/CascadeItem.vue
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,11 @@
:selected="child.selected"
:opt-label="optLabel"
:opt-value="optValue"
@update:model="onValueUpdated"
@update:model="
(value: CascadeOption[]) => {
onValueUpdated(value);
}
"
/>
</q-list>
</q-menu>
Expand Down Expand Up @@ -76,7 +80,7 @@ export default defineComponent({
this.$emit('update:model', [this.scope]);
},

onValueUpdated(selected: CascadeOption) {
onValueUpdated(selected: CascadeOption[]) {
const newSelected = [this.scope].concat(selected);
this.$emit('update:model', newSelected);
},
Expand Down
146 changes: 146 additions & 0 deletions src/components/form/SearchableMultipleSelect.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
<template>
<q-select
ref="select"
v-model="selected"
:options="options"
:placeholder="placeholder"
:option-value="optValue"
:option-label="optLabel"
filled
dense
use-input
hide-dropdown-icon
hide-bottom-space
multiple
map-options
virtual-scroll-slice-size="5"
@filter="searchOptions"
@update:model-value="(value) => clearFilter(value)"
>
<template #no-option>
<q-item>
<q-item-section class="text-grey"> 找不到任何匹配项 </q-item-section>
</q-item>
</template>
<template #selected-item="scope">
<q-chip
removable
dense
:tabindex="scope.tabindex"
color="primary"
text-color="white"
class="q-pa-sm"
:label="scope.opt.name"
@remove="scope.removeAtIndex(scope.index)"
/>
</template>
<template #option="scope">
<q-item v-bind="scope.itemProps">
<q-item-section avatar>
<boolean-chip
:value="!scope.opt.is_deleted"
true-label="正常"
false-label="禁用"
/>
</q-item-section>
<q-item-section>
<q-item-label>
{{ scope.opt[optLabel] }}
{{ scope.opt.username }}
</q-item-label>
<q-item-label caption>
{{ scope.opt.mobile }}
{{ scope.opt.email }}
</q-item-label>
<slot name="option-caption-label" :opt="scope.opt" />
</q-item-section>
<q-item-section v-if="!!scope.opt.org_type" side>
<chip-group :chips="[scope.opt.org_type]" square />
</q-item-section>
</q-item>
</template>
</q-select>
</template>

<script lang="ts">
import { defineComponent, ref } from 'vue';
import { QSelect } from 'quasar';

export default defineComponent({
name: 'SearchableMultipleSelect',

props: {
modelValue: {
type: Object,
default: () => {
return {};
},
},
optValue: {
type: String,
default: 'id',
},
optLabel: {
type: String,
default: 'name',
},
placeholder: {
type: String,
default: '输入关键字进行搜索',
},
optionApiUrl: {
type: String,
default: null,
},
optionApiParams: {
type: Object,
default: () => {
return {};
},
},
},

emits: ['update:modelValue'],

setup() {
return {
options: ref([]),
selected: ref({}),
};
},

created() {
this.selected = this.modelValue;
},

methods: {
searchOptions(
val: string,
update: (fn: () => void) => void,
abort: () => void
) {
const kw = val.trim();
if (kw === '') {
abort();
return;
}
update(async () => {
const resp = await this.$api.post(
this.optionApiUrl,
Object.assign({}, this.optionApiParams, {
q: kw,
})
);
this.options = resp.data.rows;
});
},

clearFilter(val: string) {
this.$emit('update:modelValue', val);
(this.$refs.select as QSelect).updateInputValue('');
},
},
});
</script>

<style lang="scss" scoped></style>
Loading