feat: 实现发行申请提交

This commit is contained in:
2025-12-16 03:49:39 +07:00
parent 831bc78ec5
commit 2d1454d08e
21 changed files with 870 additions and 40 deletions

View File

@@ -0,0 +1,2 @@
export { default as Collapse } from './index.vue';
export type * from './types';

View File

@@ -0,0 +1,138 @@
<script lang='ts' setup>
import type { CollapseEmits, CollapseProps } from "./types";
import { IonIcon } from "@ionic/vue";
import { chevronDownOutline } from "ionicons/icons";
import { nextTick, ref } from "vue";
const props = withDefaults(defineProps<CollapseProps>(), {
disabled: false,
bordered: true,
size: "medium",
});
defineEmits<CollapseEmits>();
const active = defineModel<boolean>("active", { type: Boolean, default: true });
const contentRef = ref<HTMLElement>();
async function toggle() {
if (props.disabled)
return;
if (!active.value) {
// 展开
active.value = true;
await nextTick();
if (contentRef.value) {
const height = contentRef.value.scrollHeight;
contentRef.value.style.height = "0px";
requestAnimationFrame(() => {
contentRef.value!.style.height = `${height}px`;
});
}
}
else {
// 收起
if (contentRef.value) {
const height = contentRef.value.scrollHeight;
contentRef.value.style.height = `${height}px`;
requestAnimationFrame(() => {
contentRef.value!.style.height = "0px";
});
}
setTimeout(() => {
active.value = false;
}, 300);
}
setTimeout(() => {
if (contentRef.value && active.value) {
contentRef.value.style.height = "auto";
}
}, 300);
}
function onTransitionEnd() {
if (contentRef.value && !active.value) {
contentRef.value.style.height = "0px";
}
}
// 暴露给父组件的方法和属性
defineExpose({
toggle,
});
</script>
<template>
<div
class="w-full overflow-hidden"
:class="{
'border border-gray-200 dark:border-gray-600 rounded-lg': bordered,
'opacity-60': disabled,
}"
>
<!-- 头部触发区域 -->
<div
class="flex items-center justify-between cursor-pointer transition-colors duration-200 select-none"
:class="[
{
'border-b border-gray-200 dark:border-gray-600': bordered,
},
{
'p-3 text-sm': size === 'small',
'p-4 text-base': size === 'medium',
'p-5 text-lg': size === 'large',
},
{
'cursor-not-allowed': disabled,
},
]"
@click="toggle"
>
<div class="font-medium text-gray-900 dark:text-white flex-1">
<slot name="title">
{{ title }}
</slot>
</div>
<IonIcon
:icon="chevronDownOutline"
class="text-gray-500 dark:text-gray-400 transition-transform duration-300"
:class="[
{
'w-4 h-4': size === 'small',
'w-5 h-5': size === 'medium',
'w-6 h-6': size === 'large',
},
{
'rotate-180': active,
},
]"
/>
</div>
<!-- 内容区域 -->
<div
ref="contentRef"
class="overflow-hidden transition-all duration-300 ease-in-out"
:style="{ height: active ? 'auto' : '0' }"
@transitionend="onTransitionEnd"
>
<div
class="p-4 text-gray-600 dark:text-gray-300 space-y-5"
>
<slot />
</div>
</div>
</div>
</template>
<style lang='css' scoped>
.select-none {
user-select: none;
}
</style>

View File

@@ -0,0 +1,29 @@
export interface CollapseProps {
/** 面板标题 */
title?: string;
/** 是否禁用 */
disabled?: boolean;
/** 是否显示边框 */
bordered?: boolean;
/** 尺寸大小 */
size?: "small" | "medium" | "large";
/** 是否展开 (v-model:active) */
active?: boolean;
}
export interface CollapseEmits {
/** 展开状态变化时触发 */
"update:active": [value: boolean];
}
export interface CollapseSlots {
/** 默认插槽 - 面板内容 */
default: () => any;
/** 标题插槽 - 自定义标题内容 */
title: () => any;
}
export interface CollapseInstance {
/** 切换展开/收起状态 */
toggle: () => void;
}

View File

@@ -0,0 +1,39 @@
<script lang='ts' setup>
import type { FieldBindingObject } from "vee-validate";
interface Props extends FieldBindingObject {
label?: string;
}
const props = defineProps<Props>();
function handleChange(value: string) {
const formattedValue = useDateFormat(value, "YYYY/MM/DD").value;
props.onChange(formattedValue);
}
</script>
<template>
<div class="flex flex-col items-start">
<ion-label class="text-sm font-bold color-(--ion-text-color-secondary) mb-3.5">
{{ props.label }}
</ion-label>
<ion-datetime-button datetime="datetime" color="primary">
<div slot="date-target">
{{ props.value }}
</div>
</ion-datetime-button>
<ion-modal :keep-contents-mounted="true">
<ion-datetime
id="datetime"
class="ui-datetime"
done-text="Done"
presentation="date"
:show-default-buttons="true"
@ion-change="handleChange($event.detail.value as string)"
/>
</ion-modal>
</div>
</template>
<style lang='css' scoped></style>

View File

@@ -1,9 +0,0 @@
<script lang='ts' setup>
</script>
<template>
hello world
</template>
<style lang='css' scoped></style>

View File

@@ -0,0 +1,22 @@
<script lang='ts' setup>
defineProps<{
icon?: string;
title?: string;
description?: string;
}>();
</script>
<template>
<div class="flex-col-center space-y-3">
<ion-icon :icon="icon" class="text-5xl text-lime-500" />
<div class="text-xl font-semibold">
{{ title }}
</div>
<p class="text-center text-text-400 max-w-xs">
{{ description }}
</p>
<slot name="extra" />
</div>
</template>
<style lang='css' scoped></style>