u
This commit is contained in:
1 parent
c26d1b3c81
commit
fac3206497
24 files changed
+356
-276
No files matched your search
+7
-1
@@ -7,9 +7,9 @@ import cn.link56.company.model.dto.DeleteCompanyUserDTO;
|
|||||||
import cn.link56.company.model.dto.QueryCompanyUserDTO;
|
import cn.link56.company.model.dto.QueryCompanyUserDTO;
|
||||||
import cn.link56.company.model.dto.UpdateCompanyUserDTO;
|
import cn.link56.company.model.dto.UpdateCompanyUserDTO;
|
||||||
import cn.link56.company.model.vo.ChatUserVO;
|
import cn.link56.company.model.vo.ChatUserVO;
|
||||||
|
import cn.link56.company.model.vo.CompanyUserProfileVO;
|
||||||
import cn.link56.company.model.vo.QueryCompanyUserVO;
|
import cn.link56.company.model.vo.QueryCompanyUserVO;
|
||||||
import cn.link56.company.service.CompanyUserService;
|
import cn.link56.company.service.CompanyUserService;
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
||||||
import org.springframework.validation.annotation.Validated;
|
import org.springframework.validation.annotation.Validated;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
@@ -51,4 +51,10 @@ public class CompanyUserController {
|
|||||||
List<ChatUserVO> data = companyUserService.listCompanyChatUsers(companyId);
|
List<ChatUserVO> data = companyUserService.listCompanyChatUsers(companyId);
|
||||||
return Result.success(data);
|
return Result.success(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GetMapping("/profile/{userId}")
|
||||||
|
public Result<CompanyUserProfileVO> getCompanyUserProfile(@PathVariable("userId") Long userId){
|
||||||
|
CompanyUserProfileVO companyUserProfileVO =companyUserService.getCompanyUserProfile(userId);
|
||||||
|
return Result.success(companyUserProfileVO);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -4,10 +4,20 @@ import cn.link56.company.model.middle.RecommendCompanyRow;
|
|||||||
import cn.link56.company.model.po.Company;
|
import cn.link56.company.model.po.Company;
|
||||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
import org.apache.ibatis.annotations.Mapper;
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
import org.apache.ibatis.annotations.Select;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@Mapper
|
@Mapper
|
||||||
public interface CompanyMapper extends BaseMapper<Company> {
|
public interface CompanyMapper extends BaseMapper<Company> {
|
||||||
List<RecommendCompanyRow> listRecommend();
|
List<RecommendCompanyRow> listRecommend();
|
||||||
|
|
||||||
|
@Select(
|
||||||
|
"SELECT c.* " +
|
||||||
|
"FROM b_company c " +
|
||||||
|
"INNER JOIN b_company_user cu ON cu.company_id = c.id " +
|
||||||
|
"WHERE cu.user_id = #{userId} "
|
||||||
|
)
|
||||||
|
Company selectCompanyByUserId(@Param("userId") Long userId);
|
||||||
}
|
}
|
||||||
+40
@@ -0,0 +1,40 @@
|
|||||||
|
package cn.link56.company.model.vo;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class CompanyUserProfileVO {
|
||||||
|
private Company company;
|
||||||
|
private User user;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
public static class Company{
|
||||||
|
private Long id;
|
||||||
|
private String name;
|
||||||
|
private String logo;
|
||||||
|
private String profile;
|
||||||
|
private List<String> areas;
|
||||||
|
private List<String> industries;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
public static class User{
|
||||||
|
private Long id;
|
||||||
|
private String name;
|
||||||
|
private String avatar;
|
||||||
|
private String position;
|
||||||
|
private String email;
|
||||||
|
private String phone;
|
||||||
|
private Boolean isOnline;
|
||||||
|
}
|
||||||
|
}
|
||||||
+41
@@ -13,6 +13,7 @@ import cn.link56.company.model.dto.UpdateCompanyUserDTO;
|
|||||||
import cn.link56.company.model.po.Company;
|
import cn.link56.company.model.po.Company;
|
||||||
import cn.link56.company.model.po.CompanyUser;
|
import cn.link56.company.model.po.CompanyUser;
|
||||||
import cn.link56.company.model.vo.ChatUserVO;
|
import cn.link56.company.model.vo.ChatUserVO;
|
||||||
|
import cn.link56.company.model.vo.CompanyUserProfileVO;
|
||||||
import cn.link56.company.model.vo.QueryCompanyUserVO;
|
import cn.link56.company.model.vo.QueryCompanyUserVO;
|
||||||
import cn.link56.im.storage.SessionManager;
|
import cn.link56.im.storage.SessionManager;
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
@@ -27,6 +28,7 @@ import org.springframework.transaction.annotation.Transactional;
|
|||||||
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@@ -327,4 +329,43 @@ public class CompanyUserService extends ServiceImpl<CompanyUserMapper, CompanyUs
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public CompanyUserProfileVO getCompanyUserProfile(Long userId) {
|
||||||
|
|
||||||
|
CompanyUser companyUserPO = companyUserMapper.selectById(userId);
|
||||||
|
if (companyUserPO == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
CompanyUserProfileVO cardVO = new CompanyUserProfileVO();
|
||||||
|
|
||||||
|
// ===== 用户(联系人)=====
|
||||||
|
CompanyUserProfileVO.User userVO = new CompanyUserProfileVO.User();
|
||||||
|
BeanUtils.copyProperties(companyUserPO, userVO);
|
||||||
|
userVO.setIsOnline(SessionManager.isOnline(userId));
|
||||||
|
|
||||||
|
// ===== 公司(主体)=====
|
||||||
|
Company companyPO = companyMapper.selectCompanyByUserId(userId);
|
||||||
|
CompanyUserProfileVO.Company companyVO = new CompanyUserProfileVO.Company();
|
||||||
|
BeanUtils.copyProperties(companyPO, companyVO);
|
||||||
|
|
||||||
|
if (companyPO.getIndustry() != null) {
|
||||||
|
companyVO.setIndustries(
|
||||||
|
Arrays.stream(companyPO.getIndustry().split(","))
|
||||||
|
.map(String::trim)
|
||||||
|
.collect(Collectors.toList())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (companyPO.getArea() != null) {
|
||||||
|
companyVO.setAreas(
|
||||||
|
Arrays.stream(companyPO.getArea().split(","))
|
||||||
|
.map(String::trim)
|
||||||
|
.collect(Collectors.toList())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
cardVO.setUser(userVO);
|
||||||
|
cardVO.setCompany(companyVO);
|
||||||
|
return cardVO;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -8,6 +8,7 @@ import cn.link56.post.model.vo.QueryPublicPostVO;
|
|||||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
import org.apache.ibatis.annotations.Mapper;
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
import org.apache.ibatis.annotations.Param;
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
import org.apache.ibatis.annotations.Update;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@@ -47,4 +48,7 @@ public interface PostMapper extends BaseMapper<Post> {
|
|||||||
@Param("title") String title,
|
@Param("title") String title,
|
||||||
@Param("tag") String tag
|
@Param("tag") String tag
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@Update("UPDATE b_post SET b_active_time = GETDATE() WHERE b_id = #{postId}")
|
||||||
|
int updateActiveTime(@Param("postId") Long postId);
|
||||||
}
|
}
|
||||||
@@ -59,4 +59,9 @@ public class Post {
|
|||||||
fill = FieldFill.INSERT_UPDATE
|
fill = FieldFill.INSERT_UPDATE
|
||||||
)
|
)
|
||||||
private Date updateTime;
|
private Date updateTime;
|
||||||
|
|
||||||
|
@TableField(
|
||||||
|
value = "b_active_time"
|
||||||
|
)
|
||||||
|
private Date activeTime;
|
||||||
}
|
}
|
||||||
+6
@@ -2,6 +2,7 @@ package cn.link56.post.service;
|
|||||||
|
|
||||||
import cn.link56.common.utils.snowflake.idgen.IdGenerator;
|
import cn.link56.common.utils.snowflake.idgen.IdGenerator;
|
||||||
import cn.link56.post.mapper.PostCommentMapper;
|
import cn.link56.post.mapper.PostCommentMapper;
|
||||||
|
import cn.link56.post.mapper.PostMapper;
|
||||||
import cn.link56.post.model.dto.CreatePostCommentDTO;
|
import cn.link56.post.model.dto.CreatePostCommentDTO;
|
||||||
import cn.link56.post.model.po.PostComment;
|
import cn.link56.post.model.po.PostComment;
|
||||||
import cn.link56.post.model.po.PostView;
|
import cn.link56.post.model.po.PostView;
|
||||||
@@ -30,6 +31,9 @@ public class PostCommentService extends ServiceImpl<PostCommentMapper, PostComme
|
|||||||
@Resource
|
@Resource
|
||||||
private PostStatService postStatService;
|
private PostStatService postStatService;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private PostMapper postMapper;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据文章 id 获取浏览总数
|
* 根据文章 id 获取浏览总数
|
||||||
*/
|
*/
|
||||||
@@ -134,6 +138,8 @@ public class PostCommentService extends ServiceImpl<PostCommentMapper, PostComme
|
|||||||
CreatePostCommentVO commentVO = new CreatePostCommentVO();
|
CreatePostCommentVO commentVO = new CreatePostCommentVO();
|
||||||
BeanUtils.copyProperties(postComment,commentVO);
|
BeanUtils.copyProperties(postComment,commentVO);
|
||||||
|
|
||||||
|
// 更新活跃数量
|
||||||
|
postMapper.updateActiveTime(postComment.getPostId());
|
||||||
return commentVO;
|
return commentVO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import cn.link56.post.model.po.Post;
|
|||||||
import cn.link56.post.model.po.PostComment;
|
import cn.link56.post.model.po.PostComment;
|
||||||
import cn.link56.post.model.vo.*;
|
import cn.link56.post.model.vo.*;
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.springframework.beans.BeanUtils;
|
import org.springframework.beans.BeanUtils;
|
||||||
@@ -47,6 +48,7 @@ public class PostService extends ServiceImpl<PostMapper, Post> {
|
|||||||
post.setId(IdGenerator.nextId());
|
post.setId(IdGenerator.nextId());
|
||||||
post.setCompanyId(UserContext.getCompanyId());
|
post.setCompanyId(UserContext.getCompanyId());
|
||||||
post.setTag(joinTags(dto.getTags()));
|
post.setTag(joinTags(dto.getTags()));
|
||||||
|
post.setActiveTime(new Date());
|
||||||
|
|
||||||
handleUpdatePublishTime(post, dto.getStatus());
|
handleUpdatePublishTime(post, dto.getStatus());
|
||||||
|
|
||||||
@@ -71,6 +73,10 @@ public class PostService extends ServiceImpl<PostMapper, Post> {
|
|||||||
handleUpdatePublishTime(post, dto.getStatus());
|
handleUpdatePublishTime(post, dto.getStatus());
|
||||||
|
|
||||||
postMapper.updateById(post);
|
postMapper.updateById(post);
|
||||||
|
|
||||||
|
// 更新活跃时间
|
||||||
|
postMapper.updateActiveTime(post.getId());
|
||||||
|
|
||||||
return buildEditPostVO(post);
|
return buildEditPostVO(post);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -250,7 +256,6 @@ public class PostService extends ServiceImpl<PostMapper, Post> {
|
|||||||
List<QueryPublicPostVO> queryEditorPostVOS = postMapper.selectPublicPostPage(offset, limit, title, tag);
|
List<QueryPublicPostVO> queryEditorPostVOS = postMapper.selectPublicPostPage(offset, limit, title, tag);
|
||||||
Long total = postMapper.selectPublicPostTotal(title, tag);
|
Long total = postMapper.selectPublicPostTotal(title, tag);
|
||||||
|
|
||||||
|
|
||||||
return new BasePageVO<QueryPublicPostVO>(total, queryEditorPostVOS);
|
return new BasePageVO<QueryPublicPostVO>(total, queryEditorPostVOS);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -275,14 +280,6 @@ public class PostService extends ServiceImpl<PostMapper, Post> {
|
|||||||
postVO.setViewCount(viewTotal);
|
postVO.setViewCount(viewTotal);
|
||||||
postVO.setCommentCount(commentTotal);
|
postVO.setCommentCount(commentTotal);
|
||||||
|
|
||||||
PostComment latestCommentByPostId = postCommentService.getLatestCommentByPostId(id);
|
|
||||||
|
|
||||||
if (latestCommentByPostId == null){
|
|
||||||
postVO.setActiveTime(post.getUpdateTime());
|
|
||||||
} else {
|
|
||||||
postVO.setActiveTime(latestCommentByPostId.getCreateTime());
|
|
||||||
}
|
|
||||||
|
|
||||||
return postVO;
|
return postVO;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -300,4 +297,5 @@ public class PostService extends ServiceImpl<PostMapper, Post> {
|
|||||||
|
|
||||||
return postCompanyVO;
|
return postCompanyVO;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
+10
-22
@@ -170,34 +170,21 @@
|
|||||||
p.b_company_id AS companyId,
|
p.b_company_id AS companyId,
|
||||||
bc.b_name AS companyName,
|
bc.b_name AS companyName,
|
||||||
|
|
||||||
ISNULL(pv.viewCount, 0) AS viewCount,
|
ISNULL(ps.b_view_count, 0) AS viewCount,
|
||||||
ISNULL(pc.commentCount, 0) AS commentCount,
|
ISNULL(ps.b_comment_count, 0) AS commentCount,
|
||||||
|
|
||||||
-- 活跃时间:最后评论时间优先
|
p.b_active_time AS activeTime,
|
||||||
ISNULL(pc.lastCommentTime, p.b_update_time) AS activeTime,
|
|
||||||
|
|
||||||
ROW_NUMBER() OVER (
|
ROW_NUMBER() OVER (
|
||||||
ORDER BY ISNULL(pc.lastCommentTime, p.b_update_time) DESC
|
ORDER BY p.b_update_time DESC
|
||||||
) AS rn
|
) AS rn
|
||||||
FROM b_post p
|
FROM b_post p
|
||||||
LEFT JOIN b_company bc ON bc.b_id = p.b_company_id
|
LEFT JOIN b_company bc
|
||||||
LEFT JOIN (
|
ON bc.b_id = p.b_company_id
|
||||||
SELECT
|
LEFT JOIN b_post_stat ps
|
||||||
b_post_id,
|
ON ps.b_post_id = p.b_id
|
||||||
COUNT(1) AS viewCount
|
|
||||||
FROM b_post_view
|
|
||||||
GROUP BY b_post_id
|
|
||||||
) pv ON pv.b_post_id = p.b_id
|
|
||||||
LEFT JOIN (
|
|
||||||
SELECT
|
|
||||||
b_post_id,
|
|
||||||
COUNT(1) AS commentCount,
|
|
||||||
MAX(b_create_time) AS lastCommentTime
|
|
||||||
FROM b_post_comment
|
|
||||||
GROUP BY b_post_id
|
|
||||||
) pc ON pc.b_post_id = p.b_id
|
|
||||||
<where>
|
<where>
|
||||||
AND p.b_status = 1
|
p.b_status = 1
|
||||||
<if test="title != null and title != ''">
|
<if test="title != null and title != ''">
|
||||||
AND p.b_title LIKE '%' + #{title} + '%'
|
AND p.b_title LIKE '%' + #{title} + '%'
|
||||||
</if>
|
</if>
|
||||||
@@ -209,6 +196,7 @@
|
|||||||
WHERE t.rn BETWEEN #{offset} AND #{limit}
|
WHERE t.rn BETWEEN #{offset} AND #{limit}
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
|
|
||||||
<select id="selectPublicPostTotal" resultType="java.lang.Long">
|
<select id="selectPublicPostTotal" resultType="java.lang.Long">
|
||||||
SELECT COUNT(1)
|
SELECT COUNT(1)
|
||||||
FROM b_post p
|
FROM b_post p
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
VITE_SERVER_BASEURL = 'http://192.168.1.137:8084'
|
VITE_SERVER_BASEURL = 'http://127.0.0.1:8084'
|
||||||
VITE_WEBSOCKET_BASEURL = 'ws://192.168.1.137:8090/ws'
|
VITE_WEBSOCKET_BASEURL = 'ws://127.0.0.1:8090/ws'
|
||||||
+5
-6
@@ -1,10 +1,9 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { CompanyChatUser } from '@/types/company/user'
|
import type { ChatCompanyUser } from '@/types/company/user'
|
||||||
import { Search } from '@element-plus/icons-vue'
|
|
||||||
import { ElMessage } from 'element-plus'
|
import { ElMessage } from 'element-plus'
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
import { listCompanyChatUsersApi } from '@/request/api/company/user'
|
import { listChatCompanyUsersApi } from '@/request/api/company/user'
|
||||||
import { createFriendApi } from '@/request/api/im'
|
import { createFriendApi } from '@/request/api/im'
|
||||||
import { useUserStore } from '@/store/user'
|
import { useUserStore } from '@/store/user'
|
||||||
|
|
||||||
@@ -19,12 +18,12 @@ const router = useRouter()
|
|||||||
const userStore = useUserStore()
|
const userStore = useUserStore()
|
||||||
|
|
||||||
const loading = ref(true)
|
const loading = ref(true)
|
||||||
const users = ref<CompanyChatUser[]>([])
|
const users = ref<ChatCompanyUser[]>([])
|
||||||
|
|
||||||
function onPopoverShow() {
|
function onPopoverShow() {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
|
|
||||||
listCompanyChatUsersApi(props.companyId).then((res) => {
|
listChatCompanyUsersApi(props.companyId).then((res) => {
|
||||||
if (res.isSuccess) {
|
if (res.isSuccess) {
|
||||||
users.value = res.data
|
users.value = res.data
|
||||||
}
|
}
|
||||||
@@ -44,7 +43,7 @@ function onPopoverHide() {
|
|||||||
users.value = []
|
users.value = []
|
||||||
}
|
}
|
||||||
|
|
||||||
function selectUser(user: CompanyChatUser) {
|
function selectUser(user: ChatCompanyUser) {
|
||||||
console.log('选择用户:', user)
|
console.log('选择用户:', user)
|
||||||
if (userStore.user.id === user.id) {
|
if (userStore.user.id === user.id) {
|
||||||
ElMessage.warning('无法与自己发起聊天')
|
ElMessage.warning('无法与自己发起聊天')
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import { ref } from 'vue'
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
userId: {
|
||||||
|
type: Number,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<el-dialog
|
||||||
|
title="Tips"
|
||||||
|
width="500"
|
||||||
|
>
|
||||||
|
<span>This is a message</span>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.loading-wrapper,
|
||||||
|
.empty-wrapper {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
height: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-wrapper {
|
||||||
|
flex-direction: column;
|
||||||
|
.empty-tip {
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -67,9 +67,7 @@ function isActive(path: string) {
|
|||||||
|
|
||||||
<el-dropdown>
|
<el-dropdown>
|
||||||
<div class="user">
|
<div class="user">
|
||||||
<el-badge v-if="websocket.connected.value" :value="12" is-dot>
|
|
||||||
<SafeImage :url="userStore.user.avatar || userStore.company.logo" :size="36" round />
|
<SafeImage :url="userStore.user.avatar || userStore.company.logo" :size="36" round />
|
||||||
</el-badge>
|
|
||||||
<div class="info">
|
<div class="info">
|
||||||
<span class="name">{{ userStore.user.name }}</span>
|
<span class="name">{{ userStore.user.name }}</span>
|
||||||
<span class="company">{{ userStore.company.name }}</span>
|
<span class="company">{{ userStore.company.name }}</span>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { AddCompanyUserParams, CompanyChatUser, CompanyUser, DeleteCompanyUserParams, QueryCompanyUserParams, UpdateCompanyUserParams } from '@/types/company/user'
|
import type { AddCompanyUserParams, ChatCompanyUser, CompanyUser, DeleteCompanyUserParams, QueryCompanyUserParams, UpdateCompanyUserParams } from '@/types/company/user'
|
||||||
import { httpGet, httpPostJson } from '@/request/utils'
|
import { httpGet, httpPostJson } from '@/request/utils'
|
||||||
|
|
||||||
export function queryCompanyUserApi(params: QueryCompanyUserParams) {
|
export function queryCompanyUserApi(params: QueryCompanyUserParams) {
|
||||||
@@ -14,6 +14,6 @@ export function deleteCompanyUserApi(params: DeleteCompanyUserParams) {
|
|||||||
return httpPostJson<void>('/company/user/delete', params)
|
return httpPostJson<void>('/company/user/delete', params)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function listCompanyChatUsersApi(companyId: number) {
|
export function listChatCompanyUsersApi(companyId: number) {
|
||||||
return httpGet<CompanyChatUser[]>(`/company/user/chat/${companyId}`)
|
return httpGet<ChatCompanyUser[]>(`/company/user/chat/${companyId}`)
|
||||||
}
|
}
|
||||||
@@ -13,8 +13,11 @@ declare module 'vue' {
|
|||||||
export interface GlobalComponents {
|
export interface GlobalComponents {
|
||||||
AnchorMenu: typeof import('./../components/anchor-menu/index.vue')['default']
|
AnchorMenu: typeof import('./../components/anchor-menu/index.vue')['default']
|
||||||
AreaSelect: typeof import('./../components/AreaSelect.vue')['default']
|
AreaSelect: typeof import('./../components/AreaSelect.vue')['default']
|
||||||
ChatCompanyUserSelect: typeof import('../components/ChatUserSelect.vue')['default']
|
ChatCompanyUserCard: typeof import('./../components/ChatCompanyUserCard.vue')['default']
|
||||||
|
ChatCompanyUserSelect: typeof i../components/ChatCompanyUserCard.vuet.vue')['default']
|
||||||
ChatUserSelect: typeof import('./../components/ChatUserSelect.vue')['default']
|
ChatUserSelect: typeof import('./../components/ChatUserSelect.vue')['default']
|
||||||
|
CompanyUserCard: typeof import('./../components/CompanyUserCard.vue')['default']
|
||||||
|
CompanyUserPopover: typeof import('./../components/CompanyUserPopover.vue')['default']
|
||||||
CustomEmpty: typeof import('./../components/CustomEmpty.vue')['default']
|
CustomEmpty: typeof import('./../components/CustomEmpty.vue')['default']
|
||||||
CustomLoading: typeof import('./../components/CustomLoading.vue')['default']
|
CustomLoading: typeof import('./../components/CustomLoading.vue')['default']
|
||||||
Directory: typeof import('./../components/wang-editor/modules/directory.vue')['default']
|
Directory: typeof import('./../components/wang-editor/modules/directory.vue')['default']
|
||||||
|
|||||||
@@ -58,10 +58,30 @@ export interface DeleteCompanyUserParams {
|
|||||||
ids: number[]
|
ids: number[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CompanyChatUser {
|
export interface ChatCompanyUser {
|
||||||
id: number
|
id: number
|
||||||
name: string
|
name: string
|
||||||
position: string
|
position: string
|
||||||
avatar: string
|
avatar: string
|
||||||
isOnline: boolean
|
isOnline: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface CompanyUserProfile {
|
||||||
|
company: {
|
||||||
|
id: number
|
||||||
|
name: string
|
||||||
|
logo: string
|
||||||
|
profile: string
|
||||||
|
areas: string[]
|
||||||
|
industries: string[]
|
||||||
|
}
|
||||||
|
user: {
|
||||||
|
id: number
|
||||||
|
name: string
|
||||||
|
position: string
|
||||||
|
avatar: string
|
||||||
|
email: string
|
||||||
|
phone: string
|
||||||
|
isOnline: boolean
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -54,11 +54,10 @@ export interface Post {
|
|||||||
tags: string[]
|
tags: string[]
|
||||||
|
|
||||||
status: number
|
status: number
|
||||||
updateTime: string
|
|
||||||
publishTime: string
|
publishTime: string
|
||||||
|
|
||||||
viewCount: number
|
viewCount: number
|
||||||
likeCount: number
|
|
||||||
favoriteCount: number
|
|
||||||
commentCount: number
|
commentCount: number
|
||||||
|
|
||||||
|
activeTime: string
|
||||||
}
|
}
|
||||||
@@ -193,7 +193,7 @@ function handleToIm(user: CompanyDetailUser) {
|
|||||||
<div class="anchor-title">
|
<div class="anchor-title">
|
||||||
导航
|
导航
|
||||||
</div>
|
</div>
|
||||||
<el-anchor :offset="70">
|
<el-anchor :offset="60">
|
||||||
<AnchorMenu :menu="anchorMenu" />
|
<AnchorMenu :menu="anchorMenu" />
|
||||||
</el-anchor>
|
</el-anchor>
|
||||||
</el-affix>
|
</el-affix>
|
||||||
|
|||||||
@@ -14,7 +14,10 @@ import { generateUUID } from '@/utils/uuid'
|
|||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
group: { type: Object as PropType<ImGroup>, required: true },
|
group: { type: Object as PropType<ImGroup>, required: true },
|
||||||
})
|
})
|
||||||
|
const emits = defineEmits<{
|
||||||
|
(e: 'avatarClick', id: number): void
|
||||||
|
(e: 'userClick', groupUser: ImGroupUser): void
|
||||||
|
}>()
|
||||||
// websocket
|
// websocket
|
||||||
const websocket = useWebSocket()
|
const websocket = useWebSocket()
|
||||||
|
|
||||||
@@ -270,6 +273,14 @@ async function sendMessage() {
|
|||||||
await nextTick()
|
await nextTick()
|
||||||
scrollToLatestMessage()
|
scrollToLatestMessage()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleClickAvatar(userId: number) {
|
||||||
|
emits('avatarClick', userId)
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleClickGroupUser(groupUser: ImGroupUser) {
|
||||||
|
emits('userClick', groupUser)
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -293,7 +304,7 @@ async function sendMessage() {
|
|||||||
? 'im__chat-message--from'
|
? 'im__chat-message--from'
|
||||||
: 'im__chat-message--to'"
|
: 'im__chat-message--to'"
|
||||||
>
|
>
|
||||||
<div class="im__chat-message-avatar">
|
<div class="im__chat-message-avatar" @click="handleClickAvatar(item.senderId)">
|
||||||
<SafeImage :url="item.senderAvatar" :size="40" round />
|
<SafeImage :url="item.senderAvatar" :size="40" round />
|
||||||
</div>
|
</div>
|
||||||
<div class="im__chat-message-body">
|
<div class="im__chat-message-body">
|
||||||
@@ -363,7 +374,7 @@ async function sendMessage() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="im__group-section-body">
|
<div class="im__group-section-body">
|
||||||
<div v-for="item in groupUsers" :key="item.userId" class="im__group-item">
|
<div v-for="item in groupUsers" :key="item.userId" class="im__group-item" @click="handleClickGroupUser(item)">
|
||||||
<el-badge
|
<el-badge
|
||||||
is-dot
|
is-dot
|
||||||
:color="item.isOnline ? '#24e68a' : '#bfbfbf'"
|
:color="item.isOnline ? '#24e68a' : '#bfbfbf'"
|
||||||
|
|||||||
@@ -15,6 +15,10 @@ const props = defineProps({
|
|||||||
friend: { type: Object as PropType<ImFriend>, required: true },
|
friend: { type: Object as PropType<ImFriend>, required: true },
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const emits = defineEmits<{
|
||||||
|
(e: 'avatarClick', id: number): void
|
||||||
|
}>()
|
||||||
|
|
||||||
// websocket
|
// websocket
|
||||||
const websocket = useWebSocket()
|
const websocket = useWebSocket()
|
||||||
|
|
||||||
@@ -253,6 +257,10 @@ async function sendMessage() {
|
|||||||
await nextTick()
|
await nextTick()
|
||||||
scrollToLatestMessage()
|
scrollToLatestMessage()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleClickAvatar(userId: number) {
|
||||||
|
emits('avatarClick', userId)
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -276,8 +284,8 @@ async function sendMessage() {
|
|||||||
? 'im__chat-message--from'
|
? 'im__chat-message--from'
|
||||||
: 'im__chat-message--to'"
|
: 'im__chat-message--to'"
|
||||||
>
|
>
|
||||||
<div class="im__chat-message-avatar">
|
<div class="im__chat-message-avatar" @click="handleClickAvatar(item.senderId)">
|
||||||
<SafeImage :url=" userStore.user.id === item.senderId ? userStore.user.avatar : props.friend.friendAvatar" :size="40" round />
|
<SafeImage :url="userStore.user.id === item.senderId ? userStore.user.avatar : props.friend.friendAvatar" :size="40" round />
|
||||||
</div>
|
</div>
|
||||||
<div class="im__chat-message-body">
|
<div class="im__chat-message-body">
|
||||||
<div class="im__chat-message-name">
|
<div class="im__chat-message-name">
|
||||||
|
|||||||
@@ -162,7 +162,7 @@ function getTotalCommentCount(list: PostComment[]): number {
|
|||||||
<template v-else>
|
<template v-else>
|
||||||
<div class="post-left">
|
<div class="post-left">
|
||||||
<el-affix :offset="90">
|
<el-affix :offset="90">
|
||||||
<el-anchor>
|
<el-anchor :offset="60">
|
||||||
<AnchorMenu :menu="anchorMenu" />
|
<AnchorMenu :menu="anchorMenu" />
|
||||||
</el-anchor>
|
</el-anchor>
|
||||||
</el-affix>
|
</el-affix>
|
||||||
|
|||||||
@@ -32,11 +32,11 @@ const menuList: NavMenuItem [] = [
|
|||||||
index: '/settings/creator',
|
index: '/settings/creator',
|
||||||
icon: 'hugeicons:edit-01',
|
icon: 'hugeicons:edit-01',
|
||||||
children: [
|
children: [
|
||||||
{
|
// {
|
||||||
label: '首页',
|
// label: '首页',
|
||||||
index: '/settings/creator/home',
|
// index: '/settings/creator/home',
|
||||||
icon: 'hugeicons:home-03',
|
// icon: 'hugeicons:home-03',
|
||||||
},
|
// },
|
||||||
{
|
{
|
||||||
label: '写文章',
|
label: '写文章',
|
||||||
index: '/settings/creator/editor',
|
index: '/settings/creator/editor',
|
||||||
|
|||||||
@@ -1,246 +1,154 @@
|
|||||||
<!doctype html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="zh-CN">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<style>
|
||||||
<title>私聊界面示例</title>
|
body {
|
||||||
<style>
|
|
||||||
body {
|
|
||||||
font-family: Arial, sans-serif;
|
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
font-family: -apple-system, BlinkMacSystemFont, "PingFang SC";
|
||||||
background-color: #f5f5f5;
|
background: #f0f2f5;
|
||||||
height: 100vh;
|
}
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 左侧聊天列表 */
|
.mask {
|
||||||
.chat-list {
|
position: fixed;
|
||||||
width: 280px;
|
inset: 0;
|
||||||
background-color: #fff;
|
background: rgba(0,0,0,.45);
|
||||||
border-right: 1px solid #ddd;
|
|
||||||
overflow-y: auto;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chat-list .user-card {
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 12px 16px;
|
justify-content: center;
|
||||||
cursor: pointer;
|
}
|
||||||
transition: background-color 0.2s;
|
|
||||||
|
.modal {
|
||||||
|
width: 480px;
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 12px 32px rgba(0,0,0,.18);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 用户区 */
|
||||||
|
.header {
|
||||||
|
display: flex;
|
||||||
|
gap: 14px;
|
||||||
|
padding: 16px 20px;
|
||||||
border-bottom: 1px solid #f0f0f0;
|
border-bottom: 1px solid #f0f0f0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.chat-list .user-card:hover,
|
.avatar {
|
||||||
.chat-list .user-card.active {
|
width: 52px;
|
||||||
background-color: #f0f8ff;
|
height: 52px;
|
||||||
}
|
|
||||||
|
|
||||||
.chat-list .avatar {
|
|
||||||
width: 40px;
|
|
||||||
height: 40px;
|
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
margin-right: 12px;
|
background: #ddd;
|
||||||
object-fit: cover;
|
}
|
||||||
border: 2px solid #4caf50;
|
|
||||||
/* 默认在线状态绿色 */
|
|
||||||
}
|
|
||||||
|
|
||||||
.chat-list .user-info {
|
.user-main {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.chat-list .user-name {
|
.user-line {
|
||||||
margin: 0;
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chat-list .user-position {
|
|
||||||
margin: 2px 0 0 0;
|
|
||||||
font-size: 12px;
|
|
||||||
color: #666;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chat-list .status {
|
|
||||||
width: 10px;
|
|
||||||
height: 10px;
|
|
||||||
border-radius: 50%;
|
|
||||||
background-color: #4caf50;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chat-list .offline {
|
|
||||||
background-color: #ccc;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 右侧聊天详情 */
|
|
||||||
.chat-detail {
|
|
||||||
flex: 1;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
background-color: #f9f9f9;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chat-header {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
padding: 12px 16px;
|
|
||||||
background-color: #fff;
|
|
||||||
border-bottom: 1px solid #ddd;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chat-header .avatar {
|
|
||||||
width: 50px;
|
|
||||||
height: 50px;
|
|
||||||
border-radius: 50%;
|
|
||||||
margin-right: 12px;
|
|
||||||
border: 2px solid #4caf50;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chat-header .info {
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chat-header .info .name {
|
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
font-weight: bold;
|
font-weight: 600;
|
||||||
margin: 0;
|
color: #262626;
|
||||||
}
|
}
|
||||||
|
|
||||||
.chat-header .info .position {
|
.user-line .role {
|
||||||
|
font-weight: 400;
|
||||||
|
color: #595959;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
color: #666;
|
color: #52c41a;
|
||||||
margin: 2px 0;
|
margin-left: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.chat-header .info .company-info {
|
.contact-line {
|
||||||
font-size: 12px;
|
margin-top: 6px;
|
||||||
color: #999;
|
font-size: 13px;
|
||||||
|
color: #595959;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
gap: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.chat-header .info .company-logo {
|
.action {
|
||||||
width: 16px;
|
margin-top: 10px;
|
||||||
height: 16px;
|
}
|
||||||
margin-right: 4px;
|
|
||||||
border-radius: 2px;
|
|
||||||
object-fit: cover;
|
|
||||||
}
|
|
||||||
|
|
||||||
.messages {
|
.btn {
|
||||||
flex: 1;
|
padding: 6px 14px;
|
||||||
padding: 16px;
|
border-radius: 6px;
|
||||||
overflow-y: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.message {
|
|
||||||
margin-bottom: 12px;
|
|
||||||
max-width: 70%;
|
|
||||||
padding: 8px 12px;
|
|
||||||
border-radius: 12px;
|
|
||||||
background-color: #fff;
|
|
||||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.message.self {
|
|
||||||
background-color: #dcf8c6;
|
|
||||||
margin-left: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.message.other {
|
|
||||||
background-color: #fff;
|
|
||||||
margin-right: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chat-input {
|
|
||||||
padding: 12px 16px;
|
|
||||||
border-top: 1px solid #ddd;
|
|
||||||
background-color: #fff;
|
|
||||||
display: flex;
|
|
||||||
gap: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chat-input input {
|
|
||||||
flex: 1;
|
|
||||||
padding: 8px 12px;
|
|
||||||
border: 1px solid #ccc;
|
|
||||||
border-radius: 20px;
|
|
||||||
outline: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chat-input button {
|
|
||||||
padding: 8px 16px;
|
|
||||||
border: none;
|
border: none;
|
||||||
background-color: #4caf50;
|
background: #1677ff;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
border-radius: 20px;
|
font-size: 13px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
/* 公司区 */
|
||||||
<!-- 左侧聊天列表 -->
|
.company {
|
||||||
<div class="chat-list">
|
padding: 14px 20px 18px;
|
||||||
<div class="user-card active">
|
}
|
||||||
<img class="avatar" src="https://i.pravatar.cc/40?img=1" alt="头像" />
|
|
||||||
<div class="user-info">
|
.company-name {
|
||||||
<p class="user-name">张三</p>
|
font-size: 14px;
|
||||||
<p class="user-position">前端工程师</p>
|
font-weight: 500;
|
||||||
</div>
|
color: #262626;
|
||||||
<div class="status"></div>
|
}
|
||||||
|
|
||||||
|
.company-meta {
|
||||||
|
margin-top: 4px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #8c8c8c;
|
||||||
|
}
|
||||||
|
|
||||||
|
.desc {
|
||||||
|
margin-top: 8px;
|
||||||
|
font-size: 13px;
|
||||||
|
color: #595959;
|
||||||
|
line-height: 1.6;
|
||||||
|
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-line-clamp: 2;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div class="mask">
|
||||||
|
<div class="modal">
|
||||||
|
|
||||||
|
<!-- 用户 -->
|
||||||
|
<div class="header">
|
||||||
|
<div class="avatar"></div>
|
||||||
|
<div class="user-main">
|
||||||
|
<div class="user-line">
|
||||||
|
张三 · <span class="role">产品经理</span>
|
||||||
|
<span class="status">● 在线</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="user-card">
|
<div class="contact-line">
|
||||||
<img class="avatar" src="https://i.pravatar.cc/40?img=2" alt="头像" />
|
<span>📧 zhangsan@company.com</span>
|
||||||
<div class="user-info">
|
<span>📞 138****8888</span>
|
||||||
<p class="user-name">李四</p>
|
|
||||||
<p class="user-position">产品经理</p>
|
|
||||||
</div>
|
|
||||||
<div class="status offline"></div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="user-card">
|
<div class="action">
|
||||||
<img class="avatar" src="https://i.pravatar.cc/40?img=3" alt="头像" />
|
<button class="btn">发起聊天</button>
|
||||||
<div class="user-info">
|
|
||||||
<p class="user-name">王五</p>
|
|
||||||
<p class="user-position">设计师</p>
|
|
||||||
</div>
|
|
||||||
<div class="status"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 右侧聊天详情 -->
|
|
||||||
<div class="chat-detail">
|
|
||||||
<!-- 聊天头部 -->
|
|
||||||
<div class="chat-header">
|
|
||||||
<img class="avatar" src="https://i.pravatar.cc/50?img=1" alt="头像" />
|
|
||||||
<div class="info">
|
|
||||||
<p class="name">张三</p>
|
|
||||||
<p class="position">前端工程师</p>
|
|
||||||
<div class="company-info">
|
|
||||||
<img class="company-logo" src="https://via.placeholder.com/16" alt="公司logo" />
|
|
||||||
<span>ABC科技公司</span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 消息区域 -->
|
<!-- 公司 -->
|
||||||
<div class="messages">
|
<div class="company">
|
||||||
<div class="message other">你好!这是张三的消息。</div>
|
<div class="company-name">北京某某科技有限公司</div>
|
||||||
<div class="message self">你好,我是自己发的消息。</div>
|
<div class="company-meta">互联网 / SaaS · 北京</div>
|
||||||
<div class="message other">我们下午开会吗?</div>
|
<div class="desc">
|
||||||
<div class="message self">好的,下午三点。</div>
|
专注于为中大型企业提供数字化解决方案,覆盖供应链、协同办公与数据分析等多个领域,
|
||||||
|
服务客户超过 3000 家。
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 输入框 -->
|
|
||||||
<div class="chat-input">
|
|
||||||
<input type="text" placeholder="输入消息..." />
|
|
||||||
<button>发送</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
@@ -41,7 +41,7 @@ export default defineConfig((configEnv) => {
|
|||||||
],
|
],
|
||||||
server: {
|
server: {
|
||||||
host: '0.0.0.0',
|
host: '0.0.0.0',
|
||||||
port: 8089,
|
port: 8080,
|
||||||
open: true,
|
open: true,
|
||||||
// 用于配置开发服务器启动时预加载的一些客户端文件,以提高初次加载的速度。
|
// 用于配置开发服务器启动时预加载的一些客户端文件,以提高初次加载的速度。
|
||||||
warmup: {
|
warmup: {
|
||||||
|
|||||||
Reference in new issue
Block a user