Commit fbf8a14a authored by 法拉51246's avatar 法拉51246

less改为scss

打印完成
大屏样式问题
parent 6a3f72ad
......@@ -11,6 +11,7 @@ import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import java.util.Optional;
/**
* 专属于 web 包的工具类
......@@ -106,7 +107,9 @@ public class WebFrameworkUtils {
if (request == null) {
return null;
}
return request.getAttribute(REQUEST_ATTRIBUTE_LOGIN_USER_NAME).toString();
return Optional.ofNullable(request.getAttribute(REQUEST_ATTRIBUTE_LOGIN_USER_NAME))
.map(Object::toString)
.orElse("");
}
/**
......
......@@ -68,7 +68,10 @@ public class DeptServiceImpl implements DeptService {
validateParentDept(updateReqVO.getId(), updateReqVO.getParentId());
// 校验部门名的唯一性
validateDeptNameUnique(updateReqVO.getId(), updateReqVO.getParentId(), updateReqVO.getName());
//如果没有leaderUserId说明是删除了leaderUserId
if (ObjectUtil.isNull(updateReqVO.getLeaderUserId())) {
updateReqVO.setLeaderUserId(null);
}
// 更新部门
DeptDO updateObj = BeanUtils.toBean(updateReqVO, DeptDO.class);
deptMapper.updateById(updateObj);
......
......@@ -13,6 +13,12 @@ import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.validation.Valid;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.Year;
import java.time.YearMonth;
import java.util.Objects;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
@Tag(name = "管理后台 - 首页信息")
......@@ -32,6 +38,8 @@ public class HomeController {
@GetMapping("/getHomeInfoFirst")
@Operation(summary = "获得首页第一排信息")
public CommonResult<HomeFirstRespVO> getHomeInfoFirst(@Valid HomeReqVO reqVO) {
LocalDateTime[] localDateTimes = parseSearchTime(reqVO.getSearchTimeString());
reqVO.setSearchTime(localDateTimes);
HomeFirstRespVO data = homeService.getHomeInfoFirst(reqVO);
return success(data);
}
......@@ -40,6 +48,8 @@ public class HomeController {
@GetMapping("/getHomeInfoBfztj")
@Operation(summary = "获得首页拜访周统计")
public CommonResult<HomeBfztjRespVO> getHomeInfoBfztj(@Valid HomeReqVO reqVO) {
LocalDateTime[] localDateTimes = parseSearchTime(reqVO.getSearchTimeString());
reqVO.setSearchTime(localDateTimes);
HomeBfztjRespVO data = homeService.getHomeInfoBfztj(reqVO);
return success(data);
}
......@@ -48,20 +58,26 @@ public class HomeController {
@GetMapping("/getHomeInfoKhxzdjzbqk")
@Operation(summary = "获得首页客户性质等级占比情况")
public CommonResult<HomeKhxzdjzbqkRespVO> getHomeInfoKhxzdjzbqk(@Valid HomeReqVO reqVO) {
LocalDateTime[] localDateTimes = parseSearchTime(reqVO.getSearchTimeString());
reqVO.setSearchTime(localDateTimes);
HomeKhxzdjzbqkRespVO data = homeService.getHomeInfoKhxzdjzbqk(reqVO);
return success(data);
}
@GetMapping("/getHomeInfoBfrjfbqk")
@Operation(summary = "获得首页拜访人均分布情况")
public CommonResult<HomeBfrjfbqkRespVO> getHomeInfoBfrjfbqk(@Valid HomeReqVO reqVO) {
HomeBfrjfbqkRespVO data = homeService.getHomeInfoBfrjfbqk(reqVO);
public CommonResult<HomeKhxzdjzbqkRespVO> getHomeInfoBfrjfbqk(@Valid HomeReqVO reqVO) {
LocalDateTime[] localDateTimes = parseSearchTime(reqVO.getSearchTimeString());
reqVO.setSearchTime(localDateTimes);
HomeKhxzdjzbqkRespVO data = homeService.getHomeInfoBfrjfbqk(reqVO);
return success(data);
}
@GetMapping("/getHomeInfoKhbffszbqk")
@Operation(summary = "获得首页客户拜访方式占比情况")
public CommonResult<HomeKhxzdjzbqkRespVO> getHomeInfoKhbffszbqk(@Valid HomeReqVO reqVO) {
LocalDateTime[] localDateTimes = parseSearchTime(reqVO.getSearchTimeString());
reqVO.setSearchTime(localDateTimes);
HomeKhxzdjzbqkRespVO data = homeService.getHomeInfoKhbffszbqk(reqVO);
return success(data);
}
......@@ -69,6 +85,8 @@ public class HomeController {
@GetMapping("/getHomeInfoKhbmzbqk")
@Operation(summary = "获得首页客户部门占比情况")
public CommonResult<HomeKhxzdjzbqkRespVO> getHomeInfoKhbmzbqk(@Valid HomeReqVO reqVO) {
LocalDateTime[] localDateTimes = parseSearchTime(reqVO.getSearchTimeString());
reqVO.setSearchTime(localDateTimes);
HomeKhxzdjzbqkRespVO data = homeService.getHomeInfoKhbmzbqk(reqVO);
return success(data);
}
......@@ -76,14 +94,18 @@ public class HomeController {
@GetMapping("/getHomeInfoKhbflxzbqk")
@Operation(summary = "获得首页客户拜访类型占比情况")
public CommonResult<HomeKhxzdjzbqkRespVO> getHomeInfoKhbflxzbqk(@Valid HomeReqVO reqVO) {
LocalDateTime[] localDateTimes = parseSearchTime(reqVO.getSearchTimeString());
reqVO.setSearchTime(localDateTimes);
HomeKhxzdjzbqkRespVO data = homeService.getHomeInfoKhbflxzbqk(reqVO);
return success(data);
}
@GetMapping("/getHomeInfoBfcplxzbqk")
@Operation(summary = "获得首页拜访产品类型占比情况")
public CommonResult<HomeBfrjfbqkRespVO> getHomeInfoBfcplxzbqk(@Valid HomeReqVO reqVO) {
HomeBfrjfbqkRespVO data = homeService.getHomeInfoBfcplxzbqk(reqVO);
public CommonResult<HomeKhxzdjzbqkRespVO> getHomeInfoBfcplxzbqk(@Valid HomeReqVO reqVO) {
LocalDateTime[] localDateTimes = parseSearchTime(reqVO.getSearchTimeString());
reqVO.setSearchTime(localDateTimes);
HomeKhxzdjzbqkRespVO data = homeService.getHomeInfoBfcplxzbqk(reqVO);
return success(data);
}
......@@ -91,6 +113,8 @@ public class HomeController {
@GetMapping("/getHomeInfoBfrtj")
@Operation(summary = "获得首页拜访日统计")
public CommonResult<HomeBfrtjRespVO> getHomeInfoBfrtj(@Valid HomeReqVO reqVO) {
LocalDateTime[] localDateTimes = parseSearchTime(reqVO.getSearchTimeString());
reqVO.setSearchTime(localDateTimes);
HomeBfrtjRespVO data = homeService.getHomeInfoBfrtj(reqVO);
return success(data);
}
......@@ -98,7 +122,37 @@ public class HomeController {
//将前端传的查询时间String[] 转换为LocalDateTime[]
public static LocalDateTime[] parseSearchTime(String[] searchTime) {
if (searchTime == null || searchTime.length != 2 ||
Objects.equals(searchTime[0], "") || Objects.equals(searchTime[1], "")) {
return null;
}
String startStr = searchTime[0];
String endStr = searchTime[1];
LocalDateTime startTime;
LocalDateTime endTime;
if (startStr.matches("^\\d{4}$") && endStr.matches("^\\d{4}$")) {
// 年
startTime = Year.parse(startStr).atDay(1).atStartOfDay();
endTime = Year.parse(endStr).atMonth(12).atEndOfMonth().atTime(23, 59, 59);
} else if (startStr.matches("^\\d{4}-\\d{2}$") && endStr.matches("^\\d{4}-\\d{2}$")) {
// 年-月
startTime = YearMonth.parse(startStr).atDay(1).atStartOfDay();
endTime = YearMonth.parse(endStr).atEndOfMonth().atTime(23, 59, 59);
} else if (startStr.matches("^\\d{4}-\\d{2}-\\d{2}$") && endStr.matches("^\\d{4}-\\d{2}-\\d{2}$")) {
// 年-月-日
startTime = LocalDate.parse(startStr).atStartOfDay();
endTime = LocalDate.parse(endStr).atTime(23, 59, 59);
} else {
throw new IllegalArgumentException("searchTime 格式非法,必须是 [yyyy] 或 [yyyy-MM] 或 [yyyy-MM-dd]");
}
return new LocalDateTime[]{startTime, endTime};
}
}
\ No newline at end of file
......@@ -20,7 +20,9 @@ public class HomeReqVO {
private String companyName;
@Schema(description = "查询时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] searchTime;
@Schema(description = "查询时间前端传入的String[]")
private String[] searchTimeString;
}
\ No newline at end of file
package cn.iocoder.yudao.module.visit.controller.admin.info;
import com.fhs.common.utils.StringUtil;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import org.springframework.validation.annotation.Validated;
......@@ -74,6 +75,10 @@ public class InfoController {
@GetMapping("/getPrintListByIds")
@Operation(summary = "获得客户拜访打印信息根据Ids")
public CommonResult<List<InfoPrintVO>> getPrintListByIds(@RequestParam("ids") String ids) {
//如果ids是空的,返回异常提示,请先选择需要打印的记录
if (StringUtil.isEmpty(ids)) {
throw new RuntimeException("请先选择需要打印的记录");
}
String[] split = ids.split(",");
//转为List
List<Long> idList = new ArrayList<>();
......@@ -86,9 +91,14 @@ public class InfoController {
@GetMapping("/getPrintListByCompanyName")
@Operation(summary = "获得客户拜访打印信息根据公司名称")
public CommonResult<InfoPrintVO> getPrintListByCompanyName(@RequestParam("companyName") String companyName) {
public CommonResult<List<InfoPrintVO>> getPrintListByCompanyName(@RequestParam("companyName") String companyName) {
InfoPrintVO info = infoService.getInfoByCompanyName(companyName);
return success(info);
List<InfoPrintVO> infoPrintVO = new ArrayList<>();
if (info == null){
return success(infoPrintVO);
}
infoPrintVO.add(info);
return success(infoPrintVO);
}
@GetMapping("/page")
......
......@@ -87,7 +87,7 @@ public class CustomerInfoServiceImpl implements CustomerInfoService {
// 更新
CustomerInfoDO updateObj = BeanUtils.toBean(updateReqVO, CustomerInfoDO.class);
CustomerInfoDO customerInfoDO = customerInfoMapper.selectByCompanyName(updateObj.getCompanyName());
if (customerInfoDO != null) {
if (customerInfoDO != null && !customerInfoDO.getId().equals(updateObj.getId())) {
throw exception(CUSTOMER_INFO_COMPANY_NAME_DUPLICATE);
}
if (updateObj.getLocationImage() != null&& updateObj.getLocationImage().startsWith("https://apis.map.qq.com/ws/staticmap/v2/")){
......
......@@ -18,7 +18,7 @@ public interface HomeService {
HomeKhxzdjzbqkRespVO getHomeInfoKhxzdjzbqk(@Valid HomeReqVO reqVO);
HomeBfrjfbqkRespVO getHomeInfoBfrjfbqk(@Valid HomeReqVO reqVO);
HomeKhxzdjzbqkRespVO getHomeInfoBfrjfbqk(@Valid HomeReqVO reqVO);
HomeKhxzdjzbqkRespVO getHomeInfoKhbffszbqk(@Valid HomeReqVO reqVO);
......@@ -26,7 +26,7 @@ public interface HomeService {
HomeKhxzdjzbqkRespVO getHomeInfoKhbflxzbqk(@Valid HomeReqVO reqVO);
HomeBfrjfbqkRespVO getHomeInfoBfcplxzbqk(@Valid HomeReqVO reqVO);
HomeKhxzdjzbqkRespVO getHomeInfoBfcplxzbqk(@Valid HomeReqVO reqVO);
HomeBfrtjRespVO getHomeInfoBfrtj(@Valid HomeReqVO reqVO);
}
\ No newline at end of file
package cn.iocoder.yudao.module.visit.service.home;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.system.api.dict.DictDataApi;
import cn.iocoder.yudao.module.system.api.dict.dto.DictDataRespDTO;
......@@ -85,8 +86,17 @@ public class HomeServiceImpl implements HomeService {
//拜访周统计
@Override
public HomeBfztjRespVO getHomeInfoBfztj(HomeReqVO reqVO) {
LocalDateTime start = reqVO.getSearchTime()[0];
LocalDateTime end = reqVO.getSearchTime()[1];
//如果查询时间是空,则start = LocalDateTime.now().minusWeeks(10);
LocalDateTime start = null;
LocalDateTime end = null;
if (reqVO.getSearchTime() == null) {
start = LocalDateTime.now().minusWeeks(10);
end = LocalDateTime.now();
}else {
start = reqVO.getSearchTime()[0];
end = reqVO.getSearchTime()[1];
}
// 对齐到每周一
start = start.with(DayOfWeek.MONDAY);
......@@ -155,7 +165,7 @@ public class HomeServiceImpl implements HomeService {
//拜访人均分布情况
@Override
public HomeBfrjfbqkRespVO getHomeInfoBfrjfbqk(HomeReqVO reqVO) {
public HomeKhxzdjzbqkRespVO getHomeInfoBfrjfbqk(HomeReqVO reqVO) {
//1.先查出所有的拜访记录
InfoPageReqVO infoPageReqVO = new InfoPageReqVO();
infoPageReqVO.setPageSize(-1);
......@@ -175,12 +185,19 @@ public class HomeServiceImpl implements HomeService {
.collect(Collectors.toList());
// 拆分为两个数组
List<String> names = top10.stream().map(Map.Entry::getKey).collect(Collectors.toList());
List<Integer> values = top10.stream().map(e -> e.getValue().intValue()).collect(Collectors.toList());
HomeBfrjfbqkRespVO respVO = new HomeBfrjfbqkRespVO();
respVO.setNames(names);
respVO.setValues(values);
// List<String> names = top10.stream().map(Map.Entry::getKey).collect(Collectors.toList());
// List<Integer> values = top10.stream().map(e -> e.getValue().intValue()).collect(Collectors.toList());
//直接成对放入
List<HomeKhxzdjzbqkRespVO.TypeCountItem> list = top10.stream()
.map(entry -> {
HomeKhxzdjzbqkRespVO.TypeCountItem item = new HomeKhxzdjzbqkRespVO.TypeCountItem();
item.setName(entry.getKey());
item.setValue(entry.getValue().intValue());
return item;
})
.collect(Collectors.toList());
HomeKhxzdjzbqkRespVO respVO = new HomeKhxzdjzbqkRespVO();
respVO.setList(list);
return respVO;
}
......@@ -296,7 +313,7 @@ public class HomeServiceImpl implements HomeService {
}
@Override
public HomeBfrjfbqkRespVO getHomeInfoBfcplxzbqk(HomeReqVO reqVO) {
public HomeKhxzdjzbqkRespVO getHomeInfoBfcplxzbqk(HomeReqVO reqVO) {
//1.先查出所有的客户信息
InfoPageReqVO infoPageReqVO = new InfoPageReqVO();
infoPageReqVO.setPageSize(-1);
......@@ -317,7 +334,13 @@ public class HomeServiceImpl implements HomeService {
.collect(Collectors.toSet());
// 3. 查询产品信息(id => 名称-规格)
List<ProductDO> productList = productMapper.selectBatchIds(productIdSet);
//判断是否为空
if (CollUtil.isEmpty(productIdSet)){
HomeKhxzdjzbqkRespVO vo = new HomeKhxzdjzbqkRespVO();
vo.setList(new ArrayList<HomeKhxzdjzbqkRespVO.TypeCountItem>());
return vo;
}
List<ProductDO> productList = productMapper.selectByIds(productIdSet);
Map<Long, String> productMap = productList.stream()
.collect(Collectors.toMap(
ProductDO::getId,
......@@ -342,13 +365,21 @@ public class HomeServiceImpl implements HomeService {
.limit(10)
.collect(Collectors.toList());
List<String> names = topList.stream().map(Map.Entry::getKey).collect(Collectors.toList());
List<Integer> values = topList.stream().map(Map.Entry::getValue).collect(Collectors.toList());
//直接成对放入list
List<HomeKhxzdjzbqkRespVO.TypeCountItem> list = topList.stream()
.map(entry -> {
HomeKhxzdjzbqkRespVO.TypeCountItem item = new HomeKhxzdjzbqkRespVO.TypeCountItem();
item.setName(entry.getKey());
item.setValue(entry.getValue());
return item;
})
.collect(Collectors.toList());
// List<String> names = topList.stream().map(Map.Entry::getKey).collect(Collectors.toList());
// List<Integer> values = topList.stream().map(Map.Entry::getValue).collect(Collectors.toList());
// 6. 构建返回结果
HomeBfrjfbqkRespVO vo = new HomeBfrjfbqkRespVO();
vo.setNames(names);
vo.setValues(values);
HomeKhxzdjzbqkRespVO vo = new HomeKhxzdjzbqkRespVO();
vo.setList(list);
return vo;
}
......
......@@ -340,7 +340,7 @@ watch(timeRange, (newVal: any) => {
getEcharts()
})
</script>
<style scoped lang="less">
<style scoped lang="scss">
/* 自定义 Element Plus 组件样式 */
.filter-item {
width: 200px;
......@@ -393,4 +393,4 @@ h3 {
width: 32%;
}
}
</style>
\ No newline at end of file
</style>
......@@ -231,7 +231,7 @@ onUnmounted(() => {
})
</script>
<style scoped lang="less">
<style scoped lang="scss">
.year-range-picker {
position: relative;
......@@ -313,4 +313,4 @@ onUnmounted(() => {
opacity: 0;
}
}
</style>
\ No newline at end of file
</style>
......@@ -125,7 +125,7 @@ const handleChartReady = (chart: any) => {
}
</script>
<style scoped lang="less">
<style scoped lang="scss">
.bar-chart {
.chart-title {
font-size: 16px;
......@@ -134,4 +134,4 @@ const handleChartReady = (chart: any) => {
color: #333;
}
}
</style>
\ No newline at end of file
</style>
......@@ -142,7 +142,7 @@ const handleChartReady = (chart: any) => {
}
</script>
<style scoped lang="less">
<style scoped lang="scss">
.line-chart {
.chart-title {
font-size: 16px;
......@@ -151,4 +151,4 @@ const handleChartReady = (chart: any) => {
color: #333;
}
}
</style>
\ No newline at end of file
</style>
......@@ -47,7 +47,7 @@ const props = defineProps({
const chartOption = computed(() => {
// console.log(props.data,"props.data");
const legendData = props.data.map((item) => item.name)
return {
tooltip: {
......@@ -135,7 +135,7 @@ const handleChartReady = (chart: echarts.ECharts) => {
}
</script>
<style scoped lang="less">
<style scoped lang="scss">
.visit-way-pie-chart {
.chart-title {
font-size: 16px;
......@@ -144,4 +144,4 @@ const handleChartReady = (chart: echarts.ECharts) => {
color: #333;
}
}
</style>
\ No newline at end of file
</style>
......@@ -116,7 +116,7 @@ const handleChartReady = (chart: any) => {
}
</script>
<style scoped lang="less">
<style scoped lang="scss">
.horizontal-bar-chart {
.chart-title {
font-size: 16px;
......@@ -125,4 +125,4 @@ const handleChartReady = (chart: any) => {
color: '#333';
}
}
</style>
\ No newline at end of file
</style>
......@@ -160,10 +160,10 @@ const formRules = reactive({
cityName: [{ required: true, message: '市名称不能为空', trigger: 'blur' }],
areaName: [{ required: true, message: '区名称不能为空', trigger: 'blur' }],
regionFullName: [{ required: true, message: '所在地区不能为空', trigger: 'blur' }],
locationText: [{ required: true, message: '详细地址不能为空', trigger: 'blur' }],
locationText: [{ required: true, message: '详细地址不能为空', trigger: 'change' }],
longitude: [{ required: true, message: '经度不能为空', trigger: 'blur' }],
latitude: [{ required: true, message: '纬度不能为空', trigger: 'blur' }],
locationImage: [{ required: true, message: '定位静态图 URL不能为空', trigger: 'blur' }],
locationImage: [{ required: true, message: '定位静态图不能为空', trigger: 'blur' }],
})
const formRef = ref() // 表单 Ref
......
......@@ -166,7 +166,12 @@
<!-- 表单弹窗:添加/修改 -->
<CustomerInfoForm ref="formRef" @success="getList" />
<!-- 打印弹窗 -->
<VisitPrint ref="visitPrintRef" :data="selectedData" />
<!-- <VisitPrint ref="visitPrintRef" :data="selectedData" />-->
<Teleport to="body">
<div v-show="false">
<PrintContent ref="printComp" :dataList="printData" />
</div>
</Teleport>
<!-- 客户信息导入对话框 -->
<CustomerImportForm ref="importFormRef" @success="getList" />
</template>
......@@ -181,6 +186,8 @@ import ProductList from "@/views/visit/util/ProductList.vue";
import {useRouter} from 'vue-router'
import {InfoApi, InfoPrintVO} from "@/api/visit/info";
import CustomerImportForm from "@/views/visit/customerinfo/CustomerImportForm.vue";
import PrintContent from "@/views/visit/util/PrintContent.vue";
import {usePrint} from "@/views/visit/util/print";
/** 客户信息 列表 */
......@@ -212,25 +219,36 @@ const importFormRef = ref()
const handleImport = () => {
importFormRef.value.open()
}
const printData = ref<InfoPrintVO[]>([])
const visitPrintRef = ref()
const printComp = ref()
// 模拟获取详情数据的方法(你应该调 InfoApi.getInfoListByIds 或类似接口)
const selectedData = ref<InfoPrintVO[]>([])
const handlePrint = async (companyName: string) => {
selectedData.value = await InfoApi.getPrintListByCompanyName(companyName)
const handlePrint = async (companyName) => {
printData.value = await InfoApi.getPrintListByCompanyName(companyName)
await nextTick()
console.log(printData.value)
//判断是否有数据
if (!selectedData.value) {
if (printData.value.length === 0) {
message.warning('该客户暂无拜访记录')
return
}
console.log(selectedData.value)
await nextTick()
visitPrintRef.value?.print()
const dom = printComp.value.printRef
if (dom) usePrint(dom)
}
// const handlePrint = async (companyName: string) => {
// selectedData.value = await InfoApi.getPrintListByCompanyName(companyName)
// //判断是否有数据
// if (!selectedData.value) {
// message.warning('该客户暂无拜访记录')
// return
// }
// console.log(selectedData.value)
// await nextTick()
// printComp.value?.print()
// }
/** 多选操作**/
const handleSelectionChange = (val) => {
multipleSelection.value = val.map(item=>item.id)
......
......@@ -123,7 +123,7 @@
:autosize="{ minRows: 4, maxRows: 6 }"
/>
</el-form-item>
<el-form-item label="服务记录图片URL列表" prop="serviceImages">
<el-form-item label="服务记录图片" prop="serviceImages">
<UploadImgs v-model="formData.serviceImages" :limit="9"/>
</el-form-item>
</el-form>
......@@ -185,10 +185,10 @@ const formRules = reactive({
cityName: [{ required: true, message: '市名称不能为空', trigger: 'blur' }],
areaName: [{ required: true, message: '区名称不能为空', trigger: 'blur' }],
regionFullName: [{ required: true, message: '所在地区不能为空', trigger: 'blur' }],
locationText: [{ required: true, message: '定位地址文字描述不能为空', trigger: 'blur' }],
locationText: [{ required: true, message: '详细地址不能为空', trigger: 'change' }],
longitude: [{ required: true, message: '经度不能为空', trigger: 'blur' }],
latitude: [{ required: true, message: '纬度不能为空', trigger: 'blur' }],
locationImage: [{ required: true, message: '定位静态图URL不能为空', trigger: 'blur' }],
locationImage: [{ required: true, message: '定位静态图不能为空', trigger: 'blur' }],
visitDate: [{ required: true, message: '拜访日期不能为空', trigger: 'blur' }],
customerStatus: [{ required: true, message: '性质等级不能为空', trigger: 'blur' }],
department: [{ required: true, message: '客户部门不能为空', trigger: 'blur' }],
......
......@@ -214,7 +214,14 @@
<!-- 表单弹窗:添加/修改 -->
<InfoForm ref="formRef" @success="getList" />
<!-- 打印弹窗 -->
<VisitPrint ref="visitPrintRef" :data="selectedData" />
<!-- <VisitPrint ref="visitPrintRef" :data="selectedData" />-->
<Teleport to="body">
<div v-show="false">
<PrintContent ref="printComp" :dataList="printData" />
</div>
</Teleport>
</template>
<script setup lang="ts">
......@@ -226,20 +233,31 @@ import InfoForm from './InfoForm.vue'
import ProductList from "@/views/visit/util/ProductList.vue";
import {useRoute} from 'vue-router'
const visitPrintRef = ref()
// 模拟获取详情数据的方法(你应该调 InfoApi.getInfoListByIds 或类似接口)
const selectedData = ref<InfoPrintVO[]>([])
import { usePrint } from '../util/print'
import PrintContent from '../util/PrintContent.vue'
//==========
const printData = ref([
{ name: '张三', date: '2025-06-01', remark: '拜访说明', image: 'https://image.xnszz.com//20250604/visit_1749022959344.png' },
{ name: '李四', date: '2025-06-03', remark: '再次拜访', image: 'https://image.xnszz.com//20250604/visit_1749022959344.png' },
])
const printComp = ref()
const handlePrint = async () => {
if (multipleSelection.value.length === 0) {
return message.warning('请先选择需要打印的记录')
}
selectedData.value = await InfoApi.getPrintListByIds(multipleSelection.value.join(','))
console.log(selectedData.value)
printData.value = await InfoApi.getPrintListByIds(multipleSelection.value.join(','))
console.log(printData.value)
await nextTick()
visitPrintRef.value?.print()
const dom = printComp.value.printRef
if (dom) usePrint(dom)
}
// 模拟获取详情数据的方法(你应该调 InfoApi.getInfoListByIds 或类似接口)
const route = useRoute()
/** 客户拜访记录 列表 */
......
<!-- PrintContent.vue -->
<template>
<div ref="printRef" style="padding: 20px; font-size: 14px;">
<div v-for="(item, index) in dataList" :key="index"
style="width: 210mm;
height: 297mm;
padding: 20mm;
border: 1px solid #eee;
margin: 20px auto;
font-size: 14px;
page-break-after: always;
display: flex;
flex-direction: column;
justify-content: space-between;"
>
<h2 style="text-align: center; margin-bottom: 16px;">渠道服务记录</h2>
<div
style="display: flex;
flex-direction: row;
gap: 20px;
flex: 1;"
>
<!-- 左侧 1/3 -->
<div
style="width: 40%;
display: flex;
flex-direction: column;
gap: 8px;
background-color: rgb(78,157,249) !important;;
color: white !important;;
padding: 10px;"
>
<div
style="height: 160px;
width: 100%;"
>
<img crossorigin="anonymous" :src="item.locationImage" class="main" alt=""
style=" width: 100%;
height: 100%;
object-fit: fill;
border: 1px solid #ccc;"
/>
</div>
<div
style="display: flex;
align-items: center;
justify-content: center;
background-color: rgba(255, 255, 255, 0.1);
padding: 12px 15px;
margin-bottom: 12px;
font-size: 16px;
border-radius: 2px;
border: rgb(95, 174, 251) 1px solid;
height: 40px;"
>业务员:{{ item.salesman }}</div>
<div
style="display: flex;
align-items: center;
justify-content: center;
background-color: rgba(255, 255, 255, 0.1);
padding: 12px 15px;
margin-bottom: 12px;
font-size: 16px;
border-radius: 2px;
border: rgb(95, 174, 251) 1px solid;
height: 40px;"
>服务数量:{{ item.serviceCount }}</div>
<div
style="display: flex;
align-items: center;
justify-content: center;
background-color: rgba(255, 255, 255, 0.1);
padding: 12px 15px;
margin-bottom: 12px;
font-size: 16px;
border-radius: 2px;
border: rgb(95, 174, 251) 1px solid;
height: 40px;"
>拜访时间记录</div>
<ul
style="list-style: none; /* 移除默认的列表符号 */
padding: 0;
margin: 0;
display: flex; /* 启用 Flex 布局 */
flex-direction: column; /* 垂直排列 */
gap: 8px; /* 项间距 */
height: 180px;"
>
<li v-for="(time, i) in item.visitDate" :key="i"
style="display: flex; /* 启用 Flex 布局 */
align-items: center; /* 垂直居中 */
justify-content: center; /* 水平居中 */
height: 40px; /* 与其他 div 高度一致 */
width: 100%; /* 需要设定宽度 */"
>{{ formatDate(time) }}</li>
</ul>
<div
style="display: flex;
align-items: center;
justify-content: center;
background-color: rgba(255, 255, 255, 0.1);
padding: 12px 15px;
margin-bottom: 12px;
font-size: 16px;
border-radius: 2px;
border: rgb(95, 174, 251) 1px solid;
height: 40px;"
>主要服务内容</div>
<ul
style="list-style: none; /* 移除默认的列表符号 */
padding: 0;
margin: 0;
display: flex; /* 启用 Flex 布局 */
flex-direction: column; /* 垂直排列 */
gap: 8px; /* 项间距 */
height: 280px;"
>
<li v-for="(content, i) in item.serviceContent" :key="i"
style="height:60px;
display: -webkit-box;
-webkit-box-orient: vertical;
overflow: hidden;
text-overflow: ellipsis;
line-height: 1.4;"
>内容{{ i + 1 }}{{ content }}</li>
</ul>
</div>
<!-- 右侧 2/3 -->
<div
style="width: 60%;
padding-left: 20px;
display: flex;
flex-direction: column;
gap: 8px;"
>
<p>客户名称:{{ item.companyName }}</p>
<p>客户部门:{{ getDictLabel(DICT_TYPE.CUSTOMER_DEPT, item.department) }}</p>
<p>推广产品:{{ item.visitProductNames }}</p>
<div style="border: 1px solid #ccc;background-color: #f9f9f9;; height: 40px; display: flex; align-items: center; justify-content: center;">
拜访签到实景图
</div>
<div
style="display: flex;
flex-direction: column;
height: 800px; /* 父容器高度,按需设置 */"
>
<img
crossorigin="anonymous"
v-for="(url, i) in getSafeImages(item.serviceImages)"
:key="i"
:src="url"
style="width: 100%;
height: 25%;
object-fit: cover;
border: 1px solid #ccc;"
/>
</div>
</div>
</div>
<div style="page-break-after: always;"></div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, defineExpose } from 'vue'
import {DICT_TYPE, getDictLabel} from "@/utils/dict";
const props = defineProps({ dataList: Array })
const printRef = ref(null)
const formatDate = (date: string | Date) => {
const d = new Date(date)
return d.toLocaleString()
}
const getSafeImages = (val?: string) => {
if (!val||val==='') return []
return val.split(',').slice(0, 4)
}
defineExpose({ printRef })
</script>
<style>
* { box-sizing: border-box; margin: 0; padding: 0; }
.print-page {
width: 210mm;
height: 297mm;
padding: 20mm;
border: 1px solid #eee;
margin: 20px auto;
font-size: 14px;
page-break-after: always;
display: flex;
flex-direction: column;
justify-content: space-between;
}
.print-page > .content {
display: flex;
flex-direction: row;
gap: 20px;
flex: 1;
}
.left {
width: 40%;
display: flex;
flex-direction: column;
gap: 8px;
background-color: rgb(78,157,249);
color: white;
padding: 10px;
}
.right {
width: 60%;
padding-left: 20px;
display: flex;
flex-direction: column;
gap: 8px;
}
.image-grid {
display: flex;
flex-direction: column;
height: 800px; /* 父容器高度,按需设置 */
}
.image-grid img {
width: 100%;
height: 25%;
object-fit: contain;
border: 1px solid #ccc;
}
/* 全局重置 ul/li 的默认样式 */
.print-page > ul {
list-style: none; /* 移除默认的列表符号 */
padding: 0;
margin: 0;
display: flex; /* 启用 Flex 布局 */
flex-direction: column; /* 垂直排列 */
gap: 8px; /* 项间距 */
}
.print-page > li {
display: flex; /* 启用 Flex 布局 */
align-items: center; /* 垂直居中 */
justify-content: center; /* 水平居中 */
height: 40px; /* 与其他 div 高度一致 */
width: 100%; /* 需要设定宽度 */
}
/* 多行文本控制 */
.info-content {
height:60px;
display: -webkit-box;
-webkit-box-orient: vertical;
overflow: hidden;
text-overflow: ellipsis;
line-height: 1.4;
}
/* 通用 Flex 居中容器 */
.flex-center {
display: flex;
align-items: center;
justify-content: center;
background-color: rgba(255, 255, 255, 0.1);
padding: 12px 15px;
margin-bottom: 12px;
font-size: 16px;
border-radius: 2px;
border: rgb(95, 174, 251) 1px solid;
}
/* 高度 40px 的容器 */
.box-40 {
height: 40px;
}
/* 无边框、无背景的容器(可选) */
.clean-box {
border: none;
background-color: transparent;
}
</style>
// src/utils/print.ts
export const usePrint = (target: HTMLElement) => {
const printWindow = window.open('', '_blank')
if (!printWindow) return
const style = `
<style>
@page { size: A4; margin: 10mm; }
body { margin: 0; padding: 0; }
</style>
`
const content = target.innerHTML
printWindow.document.write(`<html><head><title>打印</title>${style}</head><body>${content}</body></html>`)
printWindow.document.close()
const images = printWindow.document.images
let loaded = 0
const onload = () => {
loaded++
if (loaded === images.length) {
printWindow.print()
printWindow.close()
}
}
if (images.length === 0) {
printWindow.print()
printWindow.close()
} else {
Array.from(images).forEach(img => {
img.onload = img.onerror = onload
})
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment