1.2.2原版

This commit is contained in:
anian
2026-04-27 19:09:03 +08:00
commit 219938eb62
100 changed files with 14320 additions and 0 deletions
@@ -0,0 +1,335 @@
<script lang="ts" setup>
import { Toast, VButton, VModal, VSpace } from "@halo-dev/components";
import { ref, computed, nextTick, watch} from "vue";
import cloneDeep from "lodash.clonedeep";
import {doubanCoreApiClient} from "@/api";
import type { DoubanMovie } from "@/api/generated";
import {toDatetimeLocal, toISOString} from "@/utils/date";
const props = withDefaults(
defineProps<{
visible: boolean;
doubanMovie?: DoubanMovie;
}>(),
{
visible: false,
doubanMovie: undefined,
}
);
const emit = defineEmits<{
(event: "update:visible", value: boolean): void;
(event: "close"): void;
}>();
const initialFormState: DoubanMovie = {
metadata: {
name: "",
generateName: "douban-movie-",
},
faves:{
remark :"",
createTime: undefined,
score: "",
status: "done"
},
spec: {
name: "",
poster: "",
link: "",
id: "",
score: "",
year: "",
type: "",
pubdate: "",
cardSubtitle: "",
dataType: "halo",
genres: []
},
kind: "DoubanMovie",
apiVersion: "douban.moony.la/v1alpha1",
};
const formState = ref<DoubanMovie>(cloneDeep(initialFormState));
const saving = ref<boolean>(false);
const formVisible = ref(false);
const createTime = ref<string | undefined>(undefined);
const isUpdateMode = computed(() => {
return !!formState.value.metadata.creationTimestamp;
});
const modalTitle = computed(() => {
return isUpdateMode.value ? "编辑条目" : "新建条目";
});
const onVisibleChange = (visible: boolean) => {
emit("update:visible", visible);
if (!visible) {
emit("close");
}
};
const handleResetForm = () => {
formState.value = cloneDeep(initialFormState);
};
watch(
() => props.visible,
(visible) => {
if (visible) {
formVisible.value = true;
} else {
setTimeout(() => {
formVisible.value = false;
handleResetForm();
}, 200);
}
}
);
watch(
() => props.doubanMovie,
(doubanMovie) => {
if (doubanMovie) {
formState.value = cloneDeep(doubanMovie);
createTime.value = toDatetimeLocal(formState.value.faves.createTime);
}else {
createTime.value = undefined;
}
},
{
immediate: true,
}
);
watch(
() => createTime.value,
(value) => {
formState.value.faves.createTime = value ? toISOString(value) : undefined;
}
);
const annotationsFormRef = ref();
const handleSaveFriend = async () => {
annotationsFormRef.value?.handleSubmit();
await nextTick();
const { customAnnotations, annotations, customFormInvalid, specFormInvalid } =
annotationsFormRef.value || {};
if (customFormInvalid || specFormInvalid) {
return;
}
formState.value.metadata.annotations = {
...annotations,
...customAnnotations,
};
try {
saving.value = true;
if (isUpdateMode.value) {
await doubanCoreApiClient.doubanMovie.updateDoubanMovie({
name: formState.value.metadata.name,
doubanMovie: formState.value
});
} else {
await doubanCoreApiClient.doubanMovie.createDoubanMovie(
{
doubanMovie: formState.value
});
}
Toast.success("保存成功");
onVisibleChange(false);
} catch (e) {
console.error(e);
} finally {
saving.value = false;
}
};
</script>
<template>
<VModal
:title="modalTitle"
:visible="visible"
:width="650"
@update:visible="onVisibleChange"
>
<template #actions>
<slot name="append-actions" />
</template>
<FormKit
v-if="formVisible"
id="doubanMovie-form"
name="doubanMovie-form"
type="form"
:config="{ validationVisibility: 'submit' }"
@submit="handleSaveFriend"
>
<div class="md:grid md:grid-cols-4 md:gap-6">
<div class="md:col-span-1">
<div class="sticky top-0">
<span class="text-base font-medium text-gray-900"> 常规 </span>
</div>
</div>
<div class="mt-5 divide-y divide-gray-100 md:col-span-3 md:mt-0">
<td v-if="isUpdateMode">
<p><img :src="formState.spec.poster" width="100"></p>
<p>{{formState.spec.name}} <span class="db--titletag">{{formState.spec.dataType == 'db' ? '豆瓣' : formState.spec.dataType == 'tmdb' ? 'TMDB' : '手动添加'}}</span>
</p>
<p>{{formState.spec.cardSubtitle}}</p>
</td>
<FormKit
v-if="formState.spec.dataType=='halo'"
type="attachment"
v-model="formState.spec.poster"
name="poster"
validation="required"
label="封面"
></FormKit>
<FormKit
v-if="formState.spec.dataType=='halo'"
type="text"
v-model="formState.spec.name"
name="name"
validation="required"
label="标题"
></FormKit>
<FormKit
type="text"
v-model="formState.spec.link"
name="link"
validation="required"
label="链接"
></FormKit>
<FormKit
v-if="formState.spec.dataType=='halo'"
type="number"
v-model="formState.spec.score"
name="score"
validation="required"
label="评分"
max="10"
min="0"
></FormKit>
<FormKit
v-if="formState.spec.dataType=='halo'"
:options="[
{
label: '电影',
value: 'movie',
},
{
label: '图书',
value: 'book',
},
{
label: '音乐',
value: 'music',
},
{
label: '游戏',
value: 'game',
},
{
label: '舞台剧',
value: 'drama',
},
]"
label="类型"
v-model="formState.spec.type"
name="type"
type="select"
></FormKit>
<FormKit
v-if="formState.spec.dataType=='halo'"
type="textarea"
v-model="formState.spec.cardSubtitle"
name="cardSubtitle"
label="描述"
:rows="4"
validation="required|length:0,300"
></FormKit>
<FormKit
type="datetime-local"
min="0000-01-01T00:00"
max="9999-12-31T23:59"
v-model="createTime"
name="createTime"
validation="required"
label="观看时间"
></FormKit>
<FormKit
:options="[
{ label: '已看', value: 'done' },
{ label: '想看', value: 'mark' },
{ label: '在看', value: 'doing' },
]"
label="状态"
v-model="formState.faves.status"
name="status"
type="select"
></FormKit>
<FormKit
type="textarea"
v-model="formState.faves.remark"
name="remark"
label="我的短评"
:rows="4"
validation="length:0,300"
></FormKit>
<FormKit
type="number"
v-model="formState.faves.score"
name="score"
label="我的评分"
max="5"
min="0"
></FormKit>
</div>
</div>
</FormKit>
<template #footer>
<VSpace>
<VButton
:loading="saving"
type="secondary"
@click="$formkit.submit('doubanMovie-form')"
>
提交
</VButton>
<VButton @click="onVisibleChange(false)">取消</VButton>
</VSpace>
</template>
</VModal>
</template>
<style scoped lang="scss">
.db--titletag {
font-size: 13px;
display: inline-block;
color: #fff;
background-color: green;
border-radius: 2px;
line-height: 1;
padding: 2px 3px;
margin-left: 4px;
}
divide-y td {
margin-bottom: 9px;
line-height: 1.3;
padding-bottom: 1rem;
}
.divide-y td p {
margin-bottom: 6px;
}
</style>