114114 </div >
115115 </div >
116116
117- <div class =" flex items-center justify-between pt-6 border-t border-gray-100 dark:border-gray-800" >
117+ <div class =" pt-4 border-t border-gray-100 dark:border-gray-800" >
118+ <label class =" block text-xs font-mono font-bold text-gray-500 dark:text-gray-400 uppercase mb-2" >
119+ Publish Date
120+ </label >
121+ <input
122+ type =" datetime-local"
123+ v-model =" article.createdAt"
124+ class =" w-full bg-gray-50 dark:bg-[#1a1a1a] border-gray-200 dark:border-gray-700 rounded-lg text-sm focus:ring-blue-500 focus:border-blue-500 dark:text-gray-300 transition-colors"
125+ >
126+ <p class =" text-[10px] text-gray-400 mt-1 font-mono" >Leave blank for current time.</p >
127+ </div >
128+
129+ <div class =" flex items-center justify-between pt-4 border-t border-gray-100 dark:border-gray-800" >
118130 <span class =" text-sm font-bold text-gray-700 dark:text-gray-300 font-mono" >PUBLISHED</span >
119131 <button
120132 @click =" article.published = !article.published"
@@ -162,18 +174,17 @@ const article = reactive({
162174 title: ' ' ,
163175 content: ' ' ,
164176 excerpt: ' ' ,
165- coverImage: ' ' , // 存储封面图片路径
177+ coverImage: ' ' ,
166178 categoryId: ' ' ,
167179 tagIds: [],
168- published: false
180+ published: false ,
181+ createdAt: ' ' // 用于绑定日期选择器
169182});
170183
171- // 计算属性:过滤出未选中的标签
172184const availableTags = computed (() => {
173185 return tags .value .filter (tag => ! article .tagIds .includes (tag .id ));
174186});
175187
176- // 辅助函数:根据ID找标签名
177188const findTagName = (tagId ) => {
178189 const tag = tags .value .find (t => t .id === tagId);
179190 return tag ? tag .name : ' Unknown' ;
@@ -190,7 +201,6 @@ const removeTag = (tagId) => {
190201 article .tagIds = article .tagIds .filter (id => id !== tagId);
191202};
192203
193- // 保存文章
194204const saveArticle = async () => {
195205 if (! article .title .trim ()) {
196206 error .value = ' Title is required' ;
@@ -206,10 +216,12 @@ const saveArticle = async () => {
206216 title: article .title ,
207217 content: article .content ,
208218 excerpt: article .excerpt || null ,
209- coverImage: article .coverImage || null , // 这里的路径来自 ImageUploader
219+ coverImage: article .coverImage || null ,
210220 categoryId: article .categoryId || null ,
211221 tagIds: article .tagIds ,
212- status: article .published ? ' published' : ' draft'
222+ status: article .published ? ' published' : ' draft' ,
223+ // 如果有值,转换为 ISO 格式传给后端;如果为空,传 null
224+ createdAt: article .createdAt ? new Date (article .createdAt ).toISOString () : null
213225 };
214226
215227 let response;
@@ -219,15 +231,14 @@ const saveArticle = async () => {
219231 response = await api .createPost (postData);
220232 }
221233
222- // 优先找 response.id,找不到就找 response. post.id,再找不到找 response.data.id
234+ // 修复 ID 获取逻辑:兼容 post 对象包裹的情况
223235 const newId = response .id || (response .post && response .post .id ) || (response .data && response .data .id );
224236
225237 if (newId) {
226238 router .push (` /posts/${ newId} ` );
227239 } else {
228- console .error (' 无法获取新文章ID,后端返回:' , response);
229- // 如果获取失败,至少跳回首页或管理页,不要跳去 404
230- router .push (' /admin' );
240+ console .error (' 无法获取文章ID,返回数据:' , response);
241+ router .push (' /admin' ); // 失败回退到管理页
231242 }
232243
233244 } catch (err) {
@@ -239,7 +250,6 @@ const saveArticle = async () => {
239250 }
240251};
241252
242- // 加载文章详情 (编辑模式)
243253const fetchArticle = async (id ) => {
244254 try {
245255 const post = await api .getPostById (id);
@@ -248,11 +258,20 @@ const fetchArticle = async (id) => {
248258 article .title = post .title ;
249259 article .content = post .content ;
250260 article .excerpt = post .excerpt || ' ' ;
251- article .coverImage = post .coverImage || ' ' ; // 回显封面
261+ article .coverImage = post .coverImage || ' ' ;
252262 article .categoryId = post .categoryId || ' ' ;
253263 article .published = post .status === ' published' ;
254264
255- // 处理标签回显
265+ // 时间格式化逻辑
266+ if (post .createdAt ) {
267+ // 必须将 UTC 时间转换为本地时间的 YYYY-MM-DDTHH:mm 格式
268+ // 否则 datetime-local 输入框不会显示值
269+ const date = new Date (post .createdAt );
270+ const offset = date .getTimezoneOffset () * 60000 ; // 本地时区偏移量
271+ const localISOTime = (new Date (date - offset)).toISOString ().slice (0 , 16 );
272+ article .createdAt = localISOTime;
273+ }
274+
256275 if (post .tags && Array .isArray (post .tags )) {
257276 article .tagIds = post .tags .map (tag => tag .id );
258277 }
@@ -276,22 +295,17 @@ const fetchData = async () => {
276295};
277296
278297onMounted (async () => {
279- // 1. 权限检查
280298 const userStr = localStorage .getItem (' user' );
281299 const user = userStr ? JSON .parse (userStr) : null ;
282- // 兼容 role='admin' 或 isAdmin=true
283300 const isAdmin = user && (user .isAdmin === true || user .role === ' admin' );
284301
285302 if (! isAdmin) {
286- // 简单粗暴,非管理员直接踢回首页
287303 router .push (' /' );
288304 return ;
289305 }
290306
291- // 2. 加载基础数据
292307 await fetchData ();
293308
294- // 3. 如果是编辑模式,加载文章
295309 if (route .params .id ) {
296310 await fetchArticle (route .params .id );
297311 }
0 commit comments