feat(components): make story creation/editing components
This commit is contained in:
		
							parent
							
								
									52f5b4a811
								
							
						
					
					
						commit
						b2e950e798
					
				
							
								
								
									
										54
									
								
								components/story/atoms/bands.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								components/story/atoms/bands.vue
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,54 @@ | ||||
| <script setup lang="ts"> | ||||
| 	import { DefaultOptionType } from "ant-design-vue/es/select"; | ||||
| 	import { RuleExpression, useField } from "vee-validate"; | ||||
| 	import { cs } from "~/lib/client/storyFormSchema"; | ||||
| 	import { IBand } from "~/models/band"; | ||||
| 	import { log } from "~/lib/server/logger"; | ||||
| 
 | ||||
| 	import iconEl from "../icon.vue"; | ||||
| 
 | ||||
| 	const bandlist = inject<IBand[]>("bandlist"); | ||||
| 	const fname = inject<string>("curName"); | ||||
| 	const { updateBands, sb } = inject<any>("selectedBands"); | ||||
| 
 | ||||
| 	const bl = ref(bandlist || []); | ||||
| 	const options: DefaultOptionType[] = bl.value | ||||
| 		.filter((a) => a.name != "") | ||||
| 		.map((a) => ({ | ||||
| 			value: a._id, | ||||
| 			label: a.name, | ||||
| 			disabled: a.locked || false, | ||||
| 		})); | ||||
| 	let bandField = useField( | ||||
| 		fname + "bands", | ||||
| 		cs.fields.bands as unknown as MaybeRef<RuleExpression<number[]>>, | ||||
| 	); | ||||
| 	const { value, errorMessage, name, setValue } = bandField; | ||||
| 	// setValue(sb) | ||||
| </script> | ||||
| 
 | ||||
| <template> | ||||
| 	<a-form-item | ||||
| 		:validate-status="!!errorMessage ? 'error' : undefined" | ||||
| 		:help="errorMessage" | ||||
| 		label="Bands" | ||||
| 	> | ||||
| 		<a-select | ||||
| 			:allow-clear="true" | ||||
| 			mode="multiple" | ||||
| 			@change=" | ||||
| 				(val) => { | ||||
| 					// log.debug(val); | ||||
| 					updateBands(val); | ||||
| 					setValue(val as number[]); | ||||
| 				} | ||||
| 			" | ||||
| 			v-model:value="value" | ||||
| 			:options="options" | ||||
| 		> | ||||
| 			<template #removeIcon> | ||||
| 				<i class="far fa-circle-x" /> | ||||
| 			</template> | ||||
| 		</a-select> | ||||
| 	</a-form-item> | ||||
| </template> | ||||
							
								
								
									
										35
									
								
								components/story/atoms/characters.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								components/story/atoms/characters.vue
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,35 @@ | ||||
| <script setup lang="ts"> | ||||
| 	import { RuleExpression, useField } from "vee-validate"; | ||||
| 	import { cs } from "~/lib/client/storyFormSchema"; | ||||
| 	import { IBand } from "~/models/band"; | ||||
| 	const fname = inject<string>("curName"); | ||||
| 	const { sb: selectedBands } = inject<any>("selectedBands"); | ||||
| 	const allBands = inject<Ref<IBand[]>>("bandlist"); | ||||
| 	const opts = computed(() => { | ||||
| 		const unflattened: any[] = []; | ||||
| 		selectedBands?.value?.forEach((v: number) => { | ||||
| 			unflattened.push(allBands?.value.find((a) => a._id == v)?.characters); | ||||
| 		}); | ||||
| 		return unflattened.flat(Infinity).map((a) => ({ value: a, label: a })); | ||||
| 	}); | ||||
| 	const charField = useField<string[]>( | ||||
| 		fname + "characters", | ||||
| 		cs.fields.characters as unknown as MaybeRef<RuleExpression<string[]>>, | ||||
| 	); | ||||
| 	const { value, errorMessage, name: bandName, setValue } = charField; | ||||
| 	// setValue([]); | ||||
| </script> | ||||
| <template> | ||||
| 	<a-form-item | ||||
| 		:help="errorMessage" | ||||
| 		label="Characters" | ||||
| 		:name="bandName as string" | ||||
| 		:validate-status="!!errorMessage ? 'error' : undefined" | ||||
| 	> | ||||
| 		<a-select mode="multiple" :options="opts" v-model:value="value"> | ||||
| 			<template #removeIcon> | ||||
| 				<i class="far fa-circle-x" /> | ||||
| 			</template> | ||||
| 		</a-select> | ||||
| 	</a-form-item> | ||||
| </template> | ||||
							
								
								
									
										28
									
								
								components/story/atoms/genre.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								components/story/atoms/genre.vue
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,28 @@ | ||||
| <script lang="ts" setup> | ||||
| 	import { useField } from "vee-validate"; | ||||
| 	const fname = inject<string>("curName"); | ||||
| 	let data = await useApiFetch("/genres"); | ||||
| 	let opts = (data.data.value as unknown as any[]).map((a) => ({ | ||||
| 		value: a, | ||||
| 		label: a, | ||||
| 	})); | ||||
| 	const { value, errorMessage, name, setValue } = useField(fname + "genre"); | ||||
| </script> | ||||
| <template> | ||||
| 	<a-form-item | ||||
| 		:help="errorMessage" | ||||
| 		label="Genre(s)" | ||||
| 		:validate-status="!!errorMessage ? 'error' : undefined" | ||||
| 	> | ||||
| 		<a-select | ||||
| 			:allow-clear="true" | ||||
| 			:options="opts" | ||||
| 			v-model:value="value as string[]" | ||||
| 			mode="multiple" | ||||
| 		> | ||||
| 			<template #removeIcon> | ||||
| 				<i class="far fa-circle-x" /> | ||||
| 			</template> | ||||
| 		</a-select> | ||||
| 	</a-form-item> | ||||
| </template> | ||||
							
								
								
									
										50
									
								
								components/story/atoms/pairings.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								components/story/atoms/pairings.vue
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,50 @@ | ||||
| <script lang="ts" setup> | ||||
| 	import { Field, useFieldArray } from "vee-validate"; | ||||
| 	import { IBand } from "~/models/band"; | ||||
| 
 | ||||
| 	const { sb: selectedBands } = inject<any>("selectedBands"); | ||||
| 	const allBands = inject<Ref<IBand[]>>("bandlist"); | ||||
| 	const fname = inject<string>("curName"); | ||||
| 	const opts = computed(() => { | ||||
| 		const uf: any[] = []; | ||||
| 		selectedBands.value.forEach((v: number) => { | ||||
| 			uf.push(allBands?.value.find((a) => a._id == v)?.characters); | ||||
| 		}); | ||||
| 		return uf.flat(Infinity).map((a) => ({ value: a, label: a })); | ||||
| 	}); | ||||
| 	const { fields, push, remove, replace, update } = useFieldArray<string[]>( | ||||
| 		fname + "relationships", | ||||
| 	); | ||||
| 	// replace([]); | ||||
| </script> | ||||
| <template> | ||||
| 	<a-form-item label="Pairings"> | ||||
| 		<a-row | ||||
| 			:gutter="5" | ||||
| 			:wrap="true" | ||||
| 			v-for="(field, idx) in fields" | ||||
| 			:key="field.key" | ||||
| 		> | ||||
| 			<Field :name="fname + 'relationships' + `[${idx}]`"> | ||||
| 				<a-select | ||||
| 					mode="multiple" | ||||
| 					:options="opts" | ||||
| 					v-model:value="field.value as string[]" | ||||
| 					@change="(val) => update(idx, val as string[])" | ||||
| 				> | ||||
| 					<template #removeIcon> | ||||
| 						<i class="far fa-circle-x" /> | ||||
| 					</template> | ||||
| 				</a-select> | ||||
| 			</Field> | ||||
| 			<a-col :span="4"> | ||||
| 				<a-button @click="(e) => remove(idx)"> - </a-button> | ||||
| 			</a-col> | ||||
| 		</a-row> | ||||
| 		<a-row justify="end"> | ||||
| 			<a-col :span="2"> | ||||
| 				<a-button @click="(e) => push([])"> + </a-button> | ||||
| 			</a-col> | ||||
| 		</a-row> | ||||
| 	</a-form-item> | ||||
| </template> | ||||
							
								
								
									
										17
									
								
								components/story/atoms/test1.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								components/story/atoms/test1.vue
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,17 @@ | ||||
| <template> | ||||
| 	<a-select | ||||
| 		mode="multiple" | ||||
| 		style="width: 100%" | ||||
| 		placeholder="Please select" | ||||
| 		:options=" | ||||
| 			[...Array(25)].map((_, i) => ({ value: (i + 10).toString(36) + (i + 1) })) | ||||
| 		" | ||||
| 		@change="handleChange" | ||||
| 	></a-select> | ||||
| </template> | ||||
| <script lang="ts" setup> | ||||
| 	import { SelectValue } from "ant-design-vue/es/select"; | ||||
| 	import { ref } from "vue"; | ||||
| 	const handleChange = (value: SelectValue) => {}; | ||||
| 	const value = ref(["a1", "b2"]); | ||||
| </script> | ||||
							
								
								
									
										135
									
								
								components/story/create/singleChapter.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										135
									
								
								components/story/create/singleChapter.vue
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,135 @@ | ||||
| <script setup lang="ts"> | ||||
| 	import { | ||||
| 		Form as VeeForm, | ||||
| 		Field, | ||||
| 		useForm, | ||||
| 		useField, | ||||
| 		ErrorMessage, | ||||
| 	} from "vee-validate"; | ||||
| 	import { NamePath } from "ant-design-vue/es/form/interface"; | ||||
| 	import { FormChapter } from "~/lib/client/types/form/story"; | ||||
| 
 | ||||
| 	import { story, bare } from "~/lib/client/editorConfig"; | ||||
| 	import elBands from "../atoms/bands.vue"; | ||||
| 	import genre from "../atoms/genre.vue"; | ||||
| 	import elCharacters from "../atoms/characters.vue"; | ||||
| 	import elPairings from "../atoms/pairings.vue"; | ||||
| 	import uploadOrPaste from "./uploadOrPaste.vue"; | ||||
| 	// import test1 from | ||||
| 
 | ||||
| 	let { name, data } = defineProps<{ | ||||
| 		name: NamePath; | ||||
| 		data: FormChapter; | ||||
| 	}>(); | ||||
| 	let acData = toRef(data); | ||||
| 	let { data: _bands } = await useApiFetch("/bands/all"); | ||||
| 	let bands = ref(_bands); | ||||
| 	provide("curName", name + "."); | ||||
| 	provide("bandlist", bands); | ||||
| 	const selectedBands = ref(data.bands || []); | ||||
| 	const updateBands = (val) => { | ||||
| 		selectedBands.value = val; | ||||
| 		// data.value.bands = val; | ||||
| 	}; | ||||
| 	provide("selectedBands", { | ||||
| 		sb: selectedBands, | ||||
| 		updateBands, | ||||
| 	}); | ||||
| 	const wrapc = { span: 12 }; | ||||
| </script> | ||||
| <template> | ||||
| 	<div> | ||||
| 		<a-row :gutter="[10, 0]"> | ||||
| 			<a-col :span="12"> | ||||
| 				<Field | ||||
| 					:name="name + '.chapterTitle'" | ||||
| 					v-slot="{ value, field, errorMessage }" | ||||
| 				> | ||||
| 					<a-form-item | ||||
| 						:name="[field.name]" | ||||
| 						label="Chapter title" | ||||
| 						:help="errorMessage" | ||||
| 						:status="!!errorMessage ? 'error' : undefined" | ||||
| 					> | ||||
| 						<a-input v-bind="field" /> | ||||
| 					</a-form-item> | ||||
| 				</Field> | ||||
| 			</a-col> | ||||
| 			<a-col :span="12"> | ||||
| 				<el-bands /> | ||||
| 			</a-col> | ||||
| 		</a-row> | ||||
| 		<a-row :gutter="[10, 0]"> | ||||
| 			<a-col :span="12"> | ||||
| 				<base-editor | ||||
| 					v-model:val="acData.summary" | ||||
| 					:name="name + '.summary'" | ||||
| 					:wrap-col="wrapc" | ||||
| 					label="Summary" | ||||
| 					:init="bare" | ||||
| 				/> | ||||
| 			</a-col> | ||||
| 			<a-col :span="12"> | ||||
| 				<base-editor | ||||
| 					v-model:val="acData.notes" | ||||
| 					:name="name + '.notes'" | ||||
| 					:wrap-col="wrapc" | ||||
| 					label="Author's notes" | ||||
| 					:init="bare" | ||||
| 				/> | ||||
| 			</a-col> | ||||
| 		</a-row> | ||||
| 		<a-row :gutter="[10, 0]"> | ||||
| 			<a-col :span="12"> | ||||
| 				<el-characters /> | ||||
| 			</a-col> | ||||
| 			<a-col :span="12"> | ||||
| 				<el-pairings /> | ||||
| 			</a-col> | ||||
| 		</a-row> | ||||
| 		<Field | ||||
| 			:name="name + '.nsfw'" | ||||
| 			type="checkbox" | ||||
| 			:unchecked-value="false" | ||||
| 			:value="true" | ||||
| 			v-slot="{ value, field, errorMessage }" | ||||
| 		> | ||||
| 			<a-checkbox v-bind="field" v-model="field.value"> | ||||
| 				Has NSFW content | ||||
| 			</a-checkbox> | ||||
| 			<error-message :name="field.name" /> | ||||
| 		</Field> | ||||
| 		<Field | ||||
| 			:name="name + '.loggedInOnly'" | ||||
| 			type="checkbox" | ||||
| 			:unchecked-value="false" | ||||
| 			:value="true" | ||||
| 			v-slot="{ value, field, errorMessage }" | ||||
| 		> | ||||
| 			<a-checkbox v-bind="field"> Visible only to registered users </a-checkbox> | ||||
| 			<error-message :name="field.name" /> | ||||
| 		</Field> | ||||
| 		<Field | ||||
| 			:name="name + '.hidden'" | ||||
| 			type="checkbox" | ||||
| 			:unchecked-value="false" | ||||
| 			:value="true" | ||||
| 			v-slot="{ value, field, errorMessage }" | ||||
| 		> | ||||
| 			<a-tooltip> | ||||
| 				<template #title> | ||||
| 					Hides your story from everyone except you and site admins. | ||||
| 				</template> | ||||
| 				<a-checkbox v-bind="field"> Hidden </a-checkbox> | ||||
| 			</a-tooltip> | ||||
| 		</Field> | ||||
| 		<a-row> | ||||
| 			<a-col :span="24"> | ||||
| 				<genre /> | ||||
| 			</a-col> | ||||
| 		</a-row> | ||||
| 		<a-divider> Contents </a-divider> | ||||
| 		<upload-or-paste /> | ||||
| 		<Field :name="name + '.id'" v-if="!!data.id" :model-value="data.id" /> | ||||
| 	</div> | ||||
| </template> | ||||
							
								
								
									
										151
									
								
								components/story/create/storyform.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										151
									
								
								components/story/create/storyform.vue
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,151 @@ | ||||
| <script setup lang="ts"> | ||||
| 	import draggable from "vuedraggable"; | ||||
| 	import { v4 } from "uuid"; | ||||
| 	import lmove from "lodash-move"; | ||||
| 	import { Field, Form as veeForm, FieldArray, FieldEntry } from "vee-validate"; | ||||
| 	import { log } from "~/lib/server/logger"; | ||||
| 	import { storySchema } from "~/lib/client/storyFormSchema"; | ||||
| 	import { | ||||
| 		FormChapter, | ||||
| 		FormStory, | ||||
| 		defaultChapter, | ||||
| 	} from "~/lib/client/types/form/story"; | ||||
| 
 | ||||
| 	import singleChapter from "./singleChapter.vue"; | ||||
| 	import icon from "~/components/icon.vue"; | ||||
| 
 | ||||
| 	const props = defineProps<{ | ||||
| 		data: FormStory; | ||||
| 		canDraft?: boolean; | ||||
| 		endpoint: string; | ||||
| 	}>(); | ||||
| 	let drag = false; | ||||
| 	let acData = ref(props.data); | ||||
| 	const expandos = ref<string[]>([]); | ||||
| 
 | ||||
| 	function logSubmit(values, actions) { | ||||
| 		// log.debug("VALUE"); | ||||
| 		// log.debug(values); | ||||
| 		// log.debug(actions); | ||||
| 	} | ||||
| 	function onSubmit(values, actions) { | ||||
| 		logSubmit(values, actions); | ||||
| 	} | ||||
| 	function inval({ values, errors, results }) { | ||||
| 		logSubmit(values, undefined); | ||||
| 	} | ||||
| </script> | ||||
| <template> | ||||
| 	<vee-form | ||||
| 		:keep-values="true" | ||||
| 		v-slot="{ values, setFieldValue }" | ||||
| 		:validation-schema="storySchema" | ||||
| 		:initial-values="data" | ||||
| 		@submit="onSubmit" | ||||
| 		@invalid-submit="inval" | ||||
| 	> | ||||
| 		<!-- <a-form v-bind:model="acData"> --> | ||||
| 		<Field name="title" v-slot="{ value, field, errorMessage }"> | ||||
| 			<a-form-item | ||||
| 				label="Title" | ||||
| 				:help="errorMessage" | ||||
| 				:validate-status="!!errorMessage ? 'error' : undefined" | ||||
| 			> | ||||
| 				<a-input v-bind="field" :value="value" /> | ||||
| 			</a-form-item> | ||||
| 		</Field> | ||||
| 		<Field | ||||
| 			:unchecked-value="false" | ||||
| 			:value="true" | ||||
| 			type="checkbox" | ||||
| 			name="completed" | ||||
| 			v-slot="{ value, field, errorMessage }" | ||||
| 		> | ||||
| 			<a-checkbox v-bind="field"> Complete </a-checkbox> | ||||
| 		</Field> | ||||
| 		<a-divider /> | ||||
| 		<!-- <test1/> --> | ||||
| 		<field-array name="chapters" v-slot="{ fields, push, remove, move }"> | ||||
| 			<draggable | ||||
| 				:component-data="{ type: 'transtion-group' }" | ||||
| 				@start="drag = true" | ||||
| 				@end="drag = false" | ||||
| 				v-model="values.chapters" | ||||
| 				tag="div" | ||||
| 				item-key="uuidKey" | ||||
| 				@change=" | ||||
| 					(e) => { | ||||
| 						if (e.moved) { | ||||
| 							// log.debug(e.moved); | ||||
| 							move(e.moved.oldIndex, e.moved.newIndex); | ||||
| 							acData.chapters = lmove( | ||||
| 								acData.chapters, | ||||
| 								e.moved.oldIndex, | ||||
| 								e.moved.newIndex, | ||||
| 							); | ||||
| 							// log.debug(toRaw(acData.chapters.map((a) => toRaw(a)))); | ||||
| 						} | ||||
| 					} | ||||
| 				" | ||||
| 			> | ||||
| 				<template #item="{ element, index }"> | ||||
| 					<a-collapse v-model:active-key="expandos" collapsible="icon"> | ||||
| 						<a-collapse-panel :key="`${element.uuidKey}`"> | ||||
| 							<template #header> | ||||
| 								{{ values.chapters[index]?.chapterTitle || "Untitled" }} | ||||
| 								<a-button | ||||
| 									@click=" | ||||
| 										(e) => { | ||||
| 											let localFields = toRaw(fields); | ||||
| 											// log.debug(`${index} | ${element.index}`); | ||||
| 											// log.debug('fields->', localFields); | ||||
| 											acData.chapters.splice(index, 1); | ||||
| 											remove(index); | ||||
| 											// todo renumber | ||||
| 											// renumber() | ||||
| 										} | ||||
| 									" | ||||
| 								> | ||||
| 									<icon istyle="regular" name="trash" /> | ||||
| 								</a-button> | ||||
| 							</template> | ||||
| 							<single-chapter :data="element" :name="`chapters[${index}]`" /> | ||||
| 						</a-collapse-panel> | ||||
| 					</a-collapse> | ||||
| 				</template> | ||||
| 				<template #footer> | ||||
| 					<a-button | ||||
| 						@click=" | ||||
| 							(e) => { | ||||
| 								if (!Array.isArray(values.chapters)) { | ||||
| 									setFieldValue('chapters', []); | ||||
| 								} | ||||
| 								const chaps = [...toRaw(values.chapters)]; | ||||
| 								let lastIndex = chaps.length - 1; | ||||
| 								if (lastIndex < 0) lastIndex = 0; | ||||
| 								let lastChapter = chaps[lastIndex]; | ||||
| 								// log.debug('chaptrs->', chaps); | ||||
| 								// log.debug('lastIndex->', lastIndex, lastChapter); | ||||
| 								let newChapter = Object.assign({}, defaultChapter, { | ||||
| 									summary: lastChapter?.summary || '', | ||||
| 									index: (lastChapter?.index || 0) + 1, | ||||
| 									bands: lastChapter?.bands || [], | ||||
| 									characters: lastChapter?.characters || [], | ||||
| 									relationships: lastChapter?.relationships || [], | ||||
| 									uuidKey: v4(), | ||||
| 									genre: lastChapter?.genre || [], | ||||
| 								}); | ||||
| 								// log.debug('nc->', newChapter); | ||||
| 								push(newChapter); | ||||
| 								acData.chapters.push(newChapter); | ||||
| 							} | ||||
| 						" | ||||
| 					> | ||||
| 						Add chapter | ||||
| 					</a-button> | ||||
| 				</template> | ||||
| 			</draggable> | ||||
| 		</field-array> | ||||
| 		<a-button type="primary" html-type="submit">go.</a-button> | ||||
| 	</vee-form> | ||||
| </template> | ||||
							
								
								
									
										53
									
								
								components/story/create/uploadOrPaste.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								components/story/create/uploadOrPaste.vue
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,53 @@ | ||||
| <script lang="ts" setup> | ||||
| 	import { | ||||
| 		Form as VeeForm, | ||||
| 		Field as veeField, | ||||
| 		useForm, | ||||
| 		useField, | ||||
| 		ErrorMessage, | ||||
| 	} from "vee-validate"; | ||||
| 	import { story } from "~/lib/client/editorConfig"; | ||||
| 	import icon from "~/components/icon.vue"; | ||||
| 	import baseEditor from "../../baseEditor.vue"; | ||||
| 	const fname = inject<string>("curName"); | ||||
| 	const fileList = ref([]); | ||||
| 	const potField = useField(fname + "pot"); | ||||
| 	const fileField = useField(fname + "file"); | ||||
| 	const { value: pvalue } = potField; | ||||
| </script> | ||||
| <template> | ||||
| 	<a-radio-group v-model:value="pvalue"> | ||||
| 		<a-radio value="pasteOrType">Paste or type content</a-radio> | ||||
| 		<a-radio value="upload">Upload a file</a-radio> | ||||
| 	</a-radio-group> | ||||
| 	<br /> | ||||
| 	<br /> | ||||
| 	<base-editor | ||||
| 		label="" | ||||
| 		v-if="pvalue === 'pasteOrType'" | ||||
| 		:init="story" | ||||
| 		:name="fname + 'content'" | ||||
| 	/> | ||||
| 	<a-upload | ||||
| 		:with-credentials="true" | ||||
| 		v-model:file-list="fileList" | ||||
| 		v-else-if="pvalue === 'upload'" | ||||
| 		:name="fname + 'file'" | ||||
| 		accept=".doc,.docx,.md,.markdown" | ||||
| 		:max-count="1" | ||||
| 		@change=" | ||||
| 			({ file }) => { | ||||
| 				let resp = JSON.parse(file.response); | ||||
| 				if (resp.success) { | ||||
| 					fileField.setValue(resp.fileName); | ||||
| 				} | ||||
| 			} | ||||
| 		" | ||||
| 	> | ||||
| 		<a-button type="primary"> | ||||
| 			<icon istyle="regular" name="upload" /><span style="margin-left: 0.5em"> | ||||
| 				Upload a file</span | ||||
| 			> | ||||
| 		</a-button> | ||||
| 	</a-upload> | ||||
| </template> | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user