|
@@ -0,0 +1,433 @@
|
|
|
+import { Component, ViewChild } from '@angular/core';
|
|
|
+import { CommonModule } from '@angular/common';
|
|
|
+import { FormsModule } from '@angular/forms';
|
|
|
+import {
|
|
|
+ IonHeader,
|
|
|
+ IonToolbar,
|
|
|
+ IonTitle,
|
|
|
+ IonContent,
|
|
|
+ IonButtons,
|
|
|
+ IonButton,
|
|
|
+ IonTextarea,
|
|
|
+ IonIcon,
|
|
|
+ IonItem,
|
|
|
+ IonLabel,
|
|
|
+ ModalController,
|
|
|
+ IonChip,
|
|
|
+ ActionSheetController,
|
|
|
+ IonActionSheet,
|
|
|
+ IonInput,
|
|
|
+ IonPopover,
|
|
|
+ IonList,
|
|
|
+ IonRow,
|
|
|
+ IonCol,
|
|
|
+ IonGrid
|
|
|
+} from '@ionic/angular/standalone';
|
|
|
+import { addIcons } from 'ionicons';
|
|
|
+import { close, image, send, checkmark, addCircleOutline, happyOutline } from 'ionicons/icons';
|
|
|
+import { Camera, CameraResultType, CameraSource } from '@capacitor/camera';
|
|
|
+
|
|
|
+@Component({
|
|
|
+ selector: 'app-create-post-modal',
|
|
|
+ template: `
|
|
|
+ <ion-header>
|
|
|
+ <ion-toolbar>
|
|
|
+ <ion-title>发布帖子</ion-title>
|
|
|
+ <ion-buttons slot="end">
|
|
|
+ <ion-button (click)="dismiss()">
|
|
|
+ <ion-icon name="close"></ion-icon>
|
|
|
+ </ion-button>
|
|
|
+ </ion-buttons>
|
|
|
+ </ion-toolbar>
|
|
|
+ </ion-header>
|
|
|
+
|
|
|
+ <ion-content class="ion-padding">
|
|
|
+ <ion-item>
|
|
|
+ <ion-textarea
|
|
|
+ #textArea
|
|
|
+ [(ngModel)]="postContent"
|
|
|
+ placeholder="分享你的学习心得..."
|
|
|
+ [autoGrow]="true"
|
|
|
+ rows="6"
|
|
|
+ ></ion-textarea>
|
|
|
+ <!-- 表情按钮 -->
|
|
|
+ <ion-button fill="clear" slot="end" (click)="openEmojiPicker($event)">
|
|
|
+ <ion-icon name="happy-outline"></ion-icon>
|
|
|
+ </ion-button>
|
|
|
+ </ion-item>
|
|
|
+
|
|
|
+ <!-- 标签选择区域 -->
|
|
|
+ <div class="tags-section">
|
|
|
+ <div class="section-title">选择标签</div>
|
|
|
+ <div class="tags-container">
|
|
|
+ <ion-chip
|
|
|
+ *ngFor="let tag of availableTags"
|
|
|
+ [color]="selectedTags.includes(tag) ? 'primary' : 'medium'"
|
|
|
+ [outline]="!selectedTags.includes(tag)"
|
|
|
+ (click)="toggleTag(tag)"
|
|
|
+ >
|
|
|
+ <ion-label>{{tag}}</ion-label>
|
|
|
+ <ion-icon
|
|
|
+ *ngIf="selectedTags.includes(tag)"
|
|
|
+ name="checkmark"
|
|
|
+ ></ion-icon>
|
|
|
+ </ion-chip>
|
|
|
+
|
|
|
+ <!-- 添加自定义标签按钮 -->
|
|
|
+ <ion-chip outline (click)="showCustomTagInput = true" *ngIf="!showCustomTagInput">
|
|
|
+ <ion-icon name="add-circle-outline"></ion-icon>
|
|
|
+ <ion-label>自定义标签</ion-label>
|
|
|
+ </ion-chip>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 自定义标签输入框 -->
|
|
|
+ <div class="custom-tag-input" *ngIf="showCustomTagInput">
|
|
|
+ <ion-item>
|
|
|
+ <ion-input
|
|
|
+ [(ngModel)]="customTagText"
|
|
|
+ placeholder="输入标签名称(不超过10个字)"
|
|
|
+ maxlength="10"
|
|
|
+ (keyup.enter)="addCustomTag()"
|
|
|
+ ></ion-input>
|
|
|
+ <ion-button slot="end" fill="clear" (click)="addCustomTag()">
|
|
|
+ 添加
|
|
|
+ </ion-button>
|
|
|
+ <ion-button slot="end" fill="clear" (click)="showCustomTagInput = false">
|
|
|
+ 取消
|
|
|
+ </ion-button>
|
|
|
+ </ion-item>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 已选择的图片预览 -->
|
|
|
+ <div class="image-preview" *ngIf="selectedImages.length > 0">
|
|
|
+ <div class="preview-item" *ngFor="let img of selectedImages; let i = index">
|
|
|
+ <img [src]="img" alt="preview">
|
|
|
+ <ion-button fill="clear" class="delete-btn" (click)="removeImage(i)">
|
|
|
+ <ion-icon name="close"></ion-icon>
|
|
|
+ </ion-button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 表情选择器弹出框 -->
|
|
|
+ <ion-popover
|
|
|
+ #emojiPopover
|
|
|
+ [isOpen]="showEmojiPicker"
|
|
|
+ (didDismiss)="showEmojiPicker = false"
|
|
|
+ [arrow]="false"
|
|
|
+ size="auto"
|
|
|
+ >
|
|
|
+ <ng-template>
|
|
|
+ <ion-content class="emoji-picker">
|
|
|
+ <ion-grid>
|
|
|
+ <ion-row>
|
|
|
+ <ion-col size="2" *ngFor="let emoji of emojis">
|
|
|
+ <div class="emoji-item" (click)="selectEmoji(emoji)">
|
|
|
+ {{emoji}}
|
|
|
+ </div>
|
|
|
+ </ion-col>
|
|
|
+ </ion-row>
|
|
|
+ </ion-grid>
|
|
|
+ </ion-content>
|
|
|
+ </ng-template>
|
|
|
+ </ion-popover>
|
|
|
+
|
|
|
+ <div class="action-buttons">
|
|
|
+ <!-- 添加图片按钮 -->
|
|
|
+ <ion-button fill="clear" (click)="addImage()" [disabled]="selectedImages.length >= 9">
|
|
|
+ <ion-icon name="image" slot="start"></ion-icon>
|
|
|
+ 添加图片
|
|
|
+ </ion-button>
|
|
|
+
|
|
|
+ <!-- 发布按钮 -->
|
|
|
+ <ion-button (click)="publish()" [disabled]="!canPublish">
|
|
|
+ <ion-icon name="send" slot="start"></ion-icon>
|
|
|
+ 发布
|
|
|
+ </ion-button>
|
|
|
+ </div>
|
|
|
+ </ion-content>
|
|
|
+ `,
|
|
|
+ styles: [`
|
|
|
+ .image-preview {
|
|
|
+ display: grid;
|
|
|
+ grid-template-columns: repeat(3, 1fr);
|
|
|
+ gap: 8px;
|
|
|
+ padding: 16px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .preview-item {
|
|
|
+ position: relative;
|
|
|
+ aspect-ratio: 1;
|
|
|
+
|
|
|
+ img {
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ object-fit: cover;
|
|
|
+ border-radius: 8px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .delete-btn {
|
|
|
+ position: absolute;
|
|
|
+ top: -8px;
|
|
|
+ right: -8px;
|
|
|
+ --padding-start: 4px;
|
|
|
+ --padding-end: 4px;
|
|
|
+ --padding-top: 4px;
|
|
|
+ --padding-bottom: 4px;
|
|
|
+ margin: 0;
|
|
|
+
|
|
|
+ ion-icon {
|
|
|
+ font-size: 16px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .action-buttons {
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ padding: 16px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .tags-section {
|
|
|
+ margin: 16px 0;
|
|
|
+
|
|
|
+ .section-title {
|
|
|
+ font-size: 14px;
|
|
|
+ color: var(--ion-color-medium);
|
|
|
+ margin-bottom: 8px;
|
|
|
+ padding: 0 4px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .tags-container {
|
|
|
+ display: flex;
|
|
|
+ flex-wrap: wrap;
|
|
|
+ gap: 8px;
|
|
|
+ padding: 4px;
|
|
|
+
|
|
|
+ ion-chip {
|
|
|
+ margin: 0;
|
|
|
+
|
|
|
+ &.selected {
|
|
|
+ --background: var(--ion-color-primary);
|
|
|
+ --color: white;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .custom-tag-input {
|
|
|
+ margin-top: 8px;
|
|
|
+
|
|
|
+ ion-item {
|
|
|
+ --padding-start: 0;
|
|
|
+ --inner-padding-end: 0;
|
|
|
+
|
|
|
+ ion-button {
|
|
|
+ margin: 0;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .emoji-picker {
|
|
|
+ width: 280px;
|
|
|
+ max-height: 200px;
|
|
|
+
|
|
|
+ .emoji-item {
|
|
|
+ font-size: 24px;
|
|
|
+ padding: 8px;
|
|
|
+ text-align: center;
|
|
|
+ cursor: pointer;
|
|
|
+
|
|
|
+ &:hover {
|
|
|
+ background: var(--ion-color-light);
|
|
|
+ border-radius: 4px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ `],
|
|
|
+ standalone: true,
|
|
|
+ imports: [
|
|
|
+ CommonModule,
|
|
|
+ FormsModule,
|
|
|
+ IonHeader,
|
|
|
+ IonToolbar,
|
|
|
+ IonTitle,
|
|
|
+ IonContent,
|
|
|
+ IonButtons,
|
|
|
+ IonButton,
|
|
|
+ IonTextarea,
|
|
|
+ IonIcon,
|
|
|
+ IonItem,
|
|
|
+ IonLabel,
|
|
|
+ IonChip,
|
|
|
+ IonActionSheet,
|
|
|
+ IonInput,
|
|
|
+ IonPopover,
|
|
|
+ IonList,
|
|
|
+ IonRow,
|
|
|
+ IonCol,
|
|
|
+ IonGrid
|
|
|
+ ]
|
|
|
+})
|
|
|
+export class CreatePostModalComponent {
|
|
|
+ postContent = '';
|
|
|
+ selectedImages: string[] = [];
|
|
|
+ selectedTags: string[] = [];
|
|
|
+ showCustomTagInput = false;
|
|
|
+ customTagText = '';
|
|
|
+ showEmojiPicker = false;
|
|
|
+
|
|
|
+ // 文本输入区域引用
|
|
|
+ @ViewChild('textArea') textArea!: IonTextarea;
|
|
|
+ @ViewChild('emojiPopover') emojiPopover!: IonPopover;
|
|
|
+
|
|
|
+ // 预设的表情列表
|
|
|
+ emojis = [
|
|
|
+ '😊', '👍', '🎉', '💪', '📚', '💡', '✨', '🌟',
|
|
|
+ '🚀', '💻', '⭐', '🎯', '✅', '❤️', '🤔', '👨💻',
|
|
|
+ '👩💻', '🌱', '🎓', '💭', '🔥', '⚡', '🎨', '🎮'
|
|
|
+ ];
|
|
|
+
|
|
|
+ // 预设的标签列表
|
|
|
+ availableTags = [
|
|
|
+ '学习打卡', '编程技术', '心得分享', '项目展示',
|
|
|
+ '求助解惑', 'Angular', 'Ionic', 'TypeScript',
|
|
|
+ '前端开发', '每日进步', '学习方法', '技术讨论'
|
|
|
+ ];
|
|
|
+
|
|
|
+ constructor(
|
|
|
+ private modalCtrl: ModalController,
|
|
|
+ private actionSheetCtrl: ActionSheetController
|
|
|
+ ) {
|
|
|
+ addIcons({
|
|
|
+ close,
|
|
|
+ image,
|
|
|
+ send,
|
|
|
+ checkmark,
|
|
|
+ addCircleOutline,
|
|
|
+ happyOutline
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ // 添加自定义标签
|
|
|
+ addCustomTag() {
|
|
|
+ const tag = this.customTagText.trim();
|
|
|
+ if (tag && !this.availableTags.includes(tag) && !this.selectedTags.includes(tag)) {
|
|
|
+ if (this.selectedTags.length < 3) {
|
|
|
+ this.selectedTags.push(tag);
|
|
|
+ this.availableTags.push(tag);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ this.customTagText = '';
|
|
|
+ this.showCustomTagInput = false;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 打开表情选择器
|
|
|
+ openEmojiPicker(event: Event) {
|
|
|
+ this.showEmojiPicker = true;
|
|
|
+ this.emojiPopover.event = event;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 选择表情
|
|
|
+ async selectEmoji(emoji: string) {
|
|
|
+ const textarea = await this.textArea.getInputElement();
|
|
|
+ const start = textarea.selectionStart || 0;
|
|
|
+ const end = textarea.selectionEnd || 0;
|
|
|
+
|
|
|
+ this.postContent =
|
|
|
+ this.postContent.substring(0, start) +
|
|
|
+ emoji +
|
|
|
+ this.postContent.substring(end);
|
|
|
+
|
|
|
+ this.showEmojiPicker = false;
|
|
|
+
|
|
|
+ // 设置光标位置
|
|
|
+ setTimeout(() => {
|
|
|
+ textarea.setSelectionRange(start + emoji.length, start + emoji.length);
|
|
|
+ textarea.focus();
|
|
|
+ }, 0);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 切换标签选择状态
|
|
|
+ toggleTag(tag: string) {
|
|
|
+ const index = this.selectedTags.indexOf(tag);
|
|
|
+ if (index === -1) {
|
|
|
+ if (this.selectedTags.length < 3) { // 最多选择3个标签
|
|
|
+ this.selectedTags.push(tag);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ this.selectedTags.splice(index, 1);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 修改发布条件
|
|
|
+ get canPublish() {
|
|
|
+ return this.postContent.trim().length > 0 && this.selectedTags.length > 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ dismiss() {
|
|
|
+ this.modalCtrl.dismiss();
|
|
|
+ }
|
|
|
+
|
|
|
+ async addImage() {
|
|
|
+ const actionSheet = await this.actionSheetCtrl.create({
|
|
|
+ header: '选择图片来源',
|
|
|
+ buttons: [
|
|
|
+ {
|
|
|
+ text: '从相册选择',
|
|
|
+ handler: () => {
|
|
|
+ this.selectImage(CameraSource.Photos);
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ text: '拍照',
|
|
|
+ handler: () => {
|
|
|
+ this.selectImage(CameraSource.Camera);
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ text: '取消',
|
|
|
+ role: 'cancel'
|
|
|
+ }
|
|
|
+ ]
|
|
|
+ });
|
|
|
+
|
|
|
+ await actionSheet.present();
|
|
|
+ }
|
|
|
+
|
|
|
+ private async selectImage(source: CameraSource) {
|
|
|
+ try {
|
|
|
+ const image = await Camera.getPhoto({
|
|
|
+ quality: 90,
|
|
|
+ allowEditing: false,
|
|
|
+ resultType: CameraResultType.DataUrl,
|
|
|
+ source: source,
|
|
|
+ width: 1024,
|
|
|
+ correctOrientation: true
|
|
|
+ });
|
|
|
+
|
|
|
+ if (image.dataUrl) {
|
|
|
+ this.selectedImages.push(image.dataUrl);
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error('选择图片失败:', error);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ removeImage(index: number) {
|
|
|
+ this.selectedImages.splice(index, 1);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 修改发布方法,包含标签
|
|
|
+ async publish() {
|
|
|
+ if (!this.canPublish) return;
|
|
|
+
|
|
|
+ const post = {
|
|
|
+ content: this.postContent,
|
|
|
+ images: this.selectedImages,
|
|
|
+ tags: this.selectedTags
|
|
|
+ };
|
|
|
+
|
|
|
+ this.modalCtrl.dismiss(post);
|
|
|
+ }
|
|
|
+}
|